Refactor: engine and peer decoupled, engine and eventbus now per peer
and stored top level in app. Storage has read only mode. Peer and group state now event based and stored in profiles.
This commit is contained in:
parent
8be10930e7
commit
0c4bbe9ad1
83
app/app.go
83
app/app.go
|
@ -3,8 +3,10 @@ package app
|
||||||
import (
|
import (
|
||||||
"cwtch.im/cwtch/event"
|
"cwtch.im/cwtch/event"
|
||||||
"cwtch.im/cwtch/peer"
|
"cwtch.im/cwtch/peer"
|
||||||
|
"cwtch.im/cwtch/protocol/connections"
|
||||||
"cwtch.im/cwtch/storage"
|
"cwtch.im/cwtch/storage"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
||||||
|
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||||
|
@ -16,12 +18,13 @@ import (
|
||||||
|
|
||||||
type application struct {
|
type application struct {
|
||||||
peers map[string]peer.CwtchPeer
|
peers map[string]peer.CwtchPeer
|
||||||
|
storage map[string]storage.ProfileStore
|
||||||
|
engines map[string]connections.Engine
|
||||||
|
eventBuses map[string]*event.Manager
|
||||||
acn connectivity.ACN
|
acn connectivity.ACN
|
||||||
directory string
|
directory string
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
primaryonion string
|
primaryonion string
|
||||||
storage map[string]storage.ProfileStore
|
|
||||||
eventBus *event.Manager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Application is a full cwtch peer application. It allows management, usage and storage of multiple peers
|
// Application is a full cwtch peer application. It allows management, usage and storage of multiple peers
|
||||||
|
@ -32,20 +35,18 @@ type Application interface {
|
||||||
PrimaryIdentity() peer.CwtchPeer
|
PrimaryIdentity() peer.CwtchPeer
|
||||||
GetPeer(onion string) peer.CwtchPeer
|
GetPeer(onion string) peer.CwtchPeer
|
||||||
ListPeers() map[string]string
|
ListPeers() map[string]string
|
||||||
|
GetEventBus(onion string) *event.Manager
|
||||||
LaunchPeers()
|
LaunchPeers()
|
||||||
|
|
||||||
EventBus() *event.Manager
|
ShutdownPeer(string)
|
||||||
|
|
||||||
Shutdown()
|
Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewApp creates a new app with some environment awareness and initializes a Tor Manager
|
// NewApp creates a new app with some environment awareness and initializes a Tor Manager
|
||||||
func NewApp(acn connectivity.ACN, appDirectory string) Application {
|
func NewApp(acn connectivity.ACN, appDirectory string) Application {
|
||||||
log.Debugf("NewApp(%v)\n", appDirectory)
|
log.Debugf("NewApp(%v)\n", appDirectory)
|
||||||
app := &application{peers: make(map[string]peer.CwtchPeer), storage: make(map[string]storage.ProfileStore), directory: appDirectory, acn: acn}
|
app := &application{peers: make(map[string]peer.CwtchPeer), storage: make(map[string]storage.ProfileStore), engines: make(map[string]connections.Engine), eventBuses: make(map[string]*event.Manager), directory: appDirectory, acn: acn}
|
||||||
os.Mkdir(path.Join(app.directory, "profiles"), 0700)
|
os.Mkdir(path.Join(app.directory, "profiles"), 0700)
|
||||||
app.eventBus = new(event.Manager)
|
|
||||||
app.eventBus.Initialize()
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,19 +54,31 @@ func NewApp(acn connectivity.ACN, appDirectory string) Application {
|
||||||
func (app *application) CreatePeer(name string, password string) (peer.CwtchPeer, error) {
|
func (app *application) CreatePeer(name string, password string) (peer.CwtchPeer, error) {
|
||||||
log.Debugf("CreatePeer(%v)\n", name)
|
log.Debugf("CreatePeer(%v)\n", name)
|
||||||
|
|
||||||
|
eventBus := new(event.Manager)
|
||||||
|
eventBus.Initialize()
|
||||||
|
|
||||||
profile := storage.NewProfile(name)
|
profile := storage.NewProfile(name)
|
||||||
profileStore := storage.NewProfileStore(app.eventBus, path.Join(app.directory, "profiles", profile.LocalID), password, profile)
|
profileStore := storage.NewProfileWriterStore(eventBus, path.Join(app.directory, "profiles", profile.LocalID), password, profile)
|
||||||
pc := profileStore.GetProfileCopy()
|
pc := profileStore.GetProfileCopy()
|
||||||
p := peer.FromProfile(pc)
|
p := peer.FromProfile(pc)
|
||||||
p.Init(app.acn, app.eventBus)
|
|
||||||
_, exists := app.peers[p.GetProfile().Onion]
|
_, exists := app.peers[p.GetProfile().Onion]
|
||||||
if exists {
|
if exists {
|
||||||
p.Shutdown()
|
profileStore.Shutdown()
|
||||||
|
eventBus.Shutdown()
|
||||||
return nil, fmt.Errorf("Error: profile for onion %v already exists", p.GetProfile().Onion)
|
return nil, fmt.Errorf("Error: profile for onion %v already exists", p.GetProfile().Onion)
|
||||||
}
|
}
|
||||||
|
p.Init(app.acn, eventBus)
|
||||||
|
|
||||||
|
blockedPeers := profile.BlockedPeers()
|
||||||
|
// TODO: Would be nice if ProtocolEngine did not need to explicitly be given the Private Key.
|
||||||
|
identity := identity.InitializeV3(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
|
||||||
|
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, eventBus, blockedPeers)
|
||||||
|
|
||||||
app.mutex.Lock()
|
app.mutex.Lock()
|
||||||
app.peers[p.GetProfile().Onion] = p
|
app.peers[p.GetProfile().Onion] = p
|
||||||
app.storage[p.GetProfile().Onion] = profileStore
|
app.storage[p.GetProfile().Onion] = profileStore
|
||||||
|
app.engines[p.GetProfile().Onion] = engine
|
||||||
|
app.eventBuses[p.GetProfile().Onion] = eventBus
|
||||||
app.mutex.Unlock()
|
app.mutex.Unlock()
|
||||||
|
|
||||||
return p, nil
|
return p, nil
|
||||||
|
@ -79,8 +92,10 @@ func (app *application) LoadProfiles(password string) error {
|
||||||
|
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
|
|
||||||
// TODO: Per profile eventBus
|
eventBus := new(event.Manager)
|
||||||
profileStore := storage.NewProfileStore(app.eventBus, path.Join(app.directory, "profiles", file.Name()), password, nil)
|
eventBus.Initialize()
|
||||||
|
|
||||||
|
profileStore := storage.NewProfileWriterStore(eventBus, path.Join(app.directory, "profiles", file.Name()), password, nil)
|
||||||
err = profileStore.Load()
|
err = profileStore.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
|
@ -91,16 +106,23 @@ func (app *application) LoadProfiles(password string) error {
|
||||||
_, exists := app.peers[profile.Onion]
|
_, exists := app.peers[profile.Onion]
|
||||||
if exists {
|
if exists {
|
||||||
profileStore.Shutdown()
|
profileStore.Shutdown()
|
||||||
|
eventBus.Shutdown()
|
||||||
log.Errorf("profile for onion %v already exists", profile.Onion)
|
log.Errorf("profile for onion %v already exists", profile.Onion)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
peer := peer.FromProfile(profile)
|
peer := peer.FromProfile(profile)
|
||||||
peer.Init(app.acn, app.eventBus)
|
peer.Init(app.acn, eventBus)
|
||||||
|
|
||||||
|
blockedPeers := profile.BlockedPeers()
|
||||||
|
identity := identity.InitializeV3(profile.Name, &profile.Ed25519PrivateKey, &profile.Ed25519PublicKey)
|
||||||
|
engine := connections.NewProtocolEngine(identity, profile.Ed25519PrivateKey, app.acn, eventBus, blockedPeers)
|
||||||
|
|
||||||
app.mutex.Lock()
|
app.mutex.Lock()
|
||||||
app.peers[profile.Onion] = peer
|
app.peers[profile.Onion] = peer
|
||||||
app.storage[profile.Onion] = profileStore
|
app.storage[profile.Onion] = profileStore
|
||||||
|
app.engines[profile.Onion] = engine
|
||||||
|
app.eventBuses[profile.Onion] = eventBus
|
||||||
if app.primaryonion == "" {
|
if app.primaryonion == "" {
|
||||||
app.primaryonion = profile.Onion
|
app.primaryonion = profile.Onion
|
||||||
}
|
}
|
||||||
|
@ -109,10 +131,13 @@ func (app *application) LoadProfiles(password string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LaunchPeers starts each peer Listening and connecting to peers and groups
|
||||||
func (app *application) LaunchPeers() {
|
func (app *application) LaunchPeers() {
|
||||||
for _, p := range app.peers {
|
for _, p := range app.peers {
|
||||||
if !p.IsStarted() {
|
if !p.IsStarted() {
|
||||||
p.Listen()
|
p.Listen()
|
||||||
|
p.StartPeersConnections()
|
||||||
|
p.StartGroupConnections()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,20 +164,34 @@ func (app *application) GetPeer(onion string) peer.CwtchPeer {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// GetEventBus returns a cwtchPeer's event bus
|
||||||
// GetTorStatus returns tor control port bootstrap-phase status info in a map
|
func (app *application) GetEventBus(onion string) *event.Manager {
|
||||||
func (app *application) GetTorStatus() (map[string]string, error) {
|
if manager, ok := app.eventBuses[onion]; ok {
|
||||||
return app.torManager.GetStatus()
|
return manager
|
||||||
}*/
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch the app's event manager
|
// ShutdownPeer shuts down a peer and removes it from the app's management
|
||||||
func (app *application) EventBus() *event.Manager {
|
func (app *application) ShutdownPeer(onion string) {
|
||||||
return app.eventBus
|
app.mutex.Lock()
|
||||||
|
defer app.mutex.Unlock()
|
||||||
|
app.eventBuses[onion].Shutdown()
|
||||||
|
delete(app.eventBuses, onion)
|
||||||
|
app.peers[onion].Shutdown()
|
||||||
|
delete(app.peers, onion)
|
||||||
|
app.engines[onion].Shutdown()
|
||||||
|
delete(app.engines, onion)
|
||||||
|
app.storage[onion].Shutdown()
|
||||||
|
delete(app.storage, onion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown shutsdown all peers of an app and then the tormanager
|
// Shutdown shutsdown all peers of an app and then the tormanager
|
||||||
func (app *application) Shutdown() {
|
func (app *application) Shutdown() {
|
||||||
for _, peer := range app.peers {
|
for id, peer := range app.peers {
|
||||||
peer.Shutdown()
|
peer.Shutdown()
|
||||||
|
app.engines[id].Shutdown()
|
||||||
|
app.storage[id].Shutdown()
|
||||||
|
app.eventBuses[id].Shutdown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,16 +11,16 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func waitForPeerServerConnection(peer peer.CwtchPeer, server string) error {
|
func waitForPeerGroupConnection(peer peer.CwtchPeer, groupID string) error {
|
||||||
for {
|
for {
|
||||||
servers := peer.GetServers()
|
_, ok := peer.GetProfile().Groups[groupID]
|
||||||
state, ok := servers[server]
|
|
||||||
if ok {
|
if ok {
|
||||||
|
state := peer.GetGroupState(groupID)
|
||||||
if state == connections.FAILED {
|
if state == connections.FAILED {
|
||||||
return errors.New("Connection to server " + server + " failed!")
|
return errors.New("Connection to group " + groupID + " failed!")
|
||||||
}
|
}
|
||||||
if state != connections.AUTHENTICATED {
|
if state != connections.AUTHENTICATED {
|
||||||
fmt.Printf("peer %v waiting to authenticate with server %v, current state: %v\n", peer.GetProfile().Onion, server, connections.ConnectionStateName[state])
|
fmt.Printf("peer %v waiting to authenticate with group %v 's server, current state: %v\n", peer.GetProfile().Onion, groupID, connections.ConnectionStateName[state])
|
||||||
time.Sleep(time.Second * 10)
|
time.Sleep(time.Second * 10)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = waitForPeerServerConnection(botPeer, serverAddr)
|
err = waitForPeerGroupConnection(botPeer, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Could not connect to server %v: %v\n", serverAddr, err)
|
fmt.Printf("Could not connect to server %v: %v\n", serverAddr, err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"cwtch.im/cwtch/model"
|
"cwtch.im/cwtch/model"
|
||||||
"cwtch.im/cwtch/protocol/connections"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||||
|
@ -45,8 +44,8 @@ var suggestionsSelectedProfile = []prompt.Suggest{
|
||||||
{Text: "/invite", Description: "invite a new contact"},
|
{Text: "/invite", Description: "invite a new contact"},
|
||||||
{Text: "/invite-to-group", Description: "invite an existing contact to join an existing group"},
|
{Text: "/invite-to-group", Description: "invite an existing contact to join an existing group"},
|
||||||
{Text: "/accept-invite", Description: "accept the invite of a group"},
|
{Text: "/accept-invite", Description: "accept the invite of a group"},
|
||||||
{Text: "/list-servers", Description: "retrieve a list of servers and their connection status"},
|
/*{Text: "/list-servers", Description: "retrieve a list of servers and their connection status"},
|
||||||
{Text: "/list-peers", Description: "retrieve a list of peers and their connection status"},
|
{Text: "/list-peers", Description: "retrieve a list of peers and their connection status"},*/
|
||||||
{Text: "/export-group", Description: "export a group invite: prints as a string"},
|
{Text: "/export-group", Description: "export a group invite: prints as a string"},
|
||||||
{Text: "/trust", Description: "trust a peer"},
|
{Text: "/trust", Description: "trust a peer"},
|
||||||
{Text: "/block", Description: "block a peer - you will no longer see messages or connect to this peer"},
|
{Text: "/block", Description: "block a peer - you will no longer see messages or connect to this peer"},
|
||||||
|
@ -60,8 +59,8 @@ var usages = map[string]string{
|
||||||
"/list-profiles": "",
|
"/list-profiles": "",
|
||||||
"/select-profile": "/select-profile [onion]",
|
"/select-profile": "/select-profile [onion]",
|
||||||
"/quit": "",
|
"/quit": "",
|
||||||
"/list-servers": "",
|
/* "/list-servers": "",
|
||||||
"/list-peers": "",
|
"/list-peers": "",*/
|
||||||
"/list-contacts": "",
|
"/list-contacts": "",
|
||||||
"/list-groups": "",
|
"/list-groups": "",
|
||||||
"/select-group": "/select-group [groupid]",
|
"/select-group": "/select-group [groupid]",
|
||||||
|
@ -424,7 +423,7 @@ func main() {
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Error inviting peer, usage: %s\n", usages[commands[0]])
|
fmt.Printf("Error inviting peer, usage: %s\n", usages[commands[0]])
|
||||||
}
|
}
|
||||||
case "/list-peers":
|
/*case "/list-peers":
|
||||||
peers := peer.GetPeers()
|
peers := peer.GetPeers()
|
||||||
for p, s := range peers {
|
for p, s := range peers {
|
||||||
fmt.Printf("Name: %v Status: %v\n", p, connections.ConnectionStateName[s])
|
fmt.Printf("Name: %v Status: %v\n", p, connections.ConnectionStateName[s])
|
||||||
|
@ -433,7 +432,7 @@ func main() {
|
||||||
servers := peer.GetServers()
|
servers := peer.GetServers()
|
||||||
for s, st := range servers {
|
for s, st := range servers {
|
||||||
fmt.Printf("Name: %v Status: %v\n", s, connections.ConnectionStateName[st])
|
fmt.Printf("Name: %v Status: %v\n", s, connections.ConnectionStateName[st])
|
||||||
}
|
}*/
|
||||||
case "/list-contacts":
|
case "/list-contacts":
|
||||||
contacts := peer.GetContacts()
|
contacts := peer.GetContacts()
|
||||||
for _, onion := range contacts {
|
for _, onion := range contacts {
|
||||||
|
|
|
@ -84,6 +84,14 @@ const (
|
||||||
// Key [eg "nick"]
|
// Key [eg "nick"]
|
||||||
// Data [eg "open privacy board"]
|
// Data [eg "open privacy board"]
|
||||||
SetGroupAttribute = Type("SetGroupAttribute")
|
SetGroupAttribute = Type("SetGroupAttribute")
|
||||||
|
|
||||||
|
// RemotePeer
|
||||||
|
// ConnectionState
|
||||||
|
PeerStateChange = Type("PeerStateChange")
|
||||||
|
|
||||||
|
// GroupServer
|
||||||
|
// ConnectionState
|
||||||
|
ServerStateChange = Type("GroupStateChange")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Field defines common event attributes
|
// Field defines common event attributes
|
||||||
|
@ -106,6 +114,8 @@ const (
|
||||||
|
|
||||||
ProfileName = Field("ProfileName")
|
ProfileName = Field("ProfileName")
|
||||||
|
|
||||||
|
ConnectionState = Field("ConnectionState")
|
||||||
|
|
||||||
Key = Field("Key")
|
Key = Field("Key")
|
||||||
Data = Field("Data")
|
Data = Field("Data")
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ type Manager struct {
|
||||||
events chan Event
|
events chan Event
|
||||||
mapMutex sync.Mutex
|
mapMutex sync.Mutex
|
||||||
internal chan bool
|
internal chan bool
|
||||||
|
closed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize sets up the Manager.
|
// Initialize sets up the Manager.
|
||||||
|
@ -31,6 +32,7 @@ func (em *Manager) Initialize() {
|
||||||
em.subscribers = make(map[Type][]chan Event)
|
em.subscribers = make(map[Type][]chan Event)
|
||||||
em.events = make(chan Event)
|
em.events = make(chan Event)
|
||||||
em.internal = make(chan bool)
|
em.internal = make(chan bool)
|
||||||
|
em.closed = false
|
||||||
go em.eventBus()
|
go em.eventBus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +46,7 @@ func (em *Manager) Subscribe(eventType Type, eventChannel chan Event) {
|
||||||
|
|
||||||
// Publish takes an Event and sends it to the internal eventBus where it is distributed to all Subscribers
|
// Publish takes an Event and sends it to the internal eventBus where it is distributed to all Subscribers
|
||||||
func (em *Manager) Publish(event Event) {
|
func (em *Manager) Publish(event Event) {
|
||||||
if event.EventType != "" {
|
if event.EventType != "" && em.closed != true {
|
||||||
em.events <- event
|
em.events <- event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,6 +85,7 @@ func (em *Manager) eventBus() {
|
||||||
// Shutdown triggers, and waits for, the internal eventBus goroutine to finish
|
// Shutdown triggers, and waits for, the internal eventBus goroutine to finish
|
||||||
func (em *Manager) Shutdown() {
|
func (em *Manager) Shutdown() {
|
||||||
em.events <- Event{}
|
em.events <- Event{}
|
||||||
|
em.closed = true
|
||||||
// wait for eventBus to finish
|
// wait for eventBus to finish
|
||||||
<-em.internal
|
<-em.internal
|
||||||
close(em.events)
|
close(em.events)
|
||||||
|
|
|
@ -29,6 +29,7 @@ type Group struct {
|
||||||
Attributes map[string]string
|
Attributes map[string]string
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
LocalID string
|
LocalID string
|
||||||
|
State string `json:"-"`
|
||||||
unacknowledgedMessages []Message
|
unacknowledgedMessages []Message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ type PublicProfile struct {
|
||||||
Attributes map[string]string
|
Attributes map[string]string
|
||||||
Timeline Timeline
|
Timeline Timeline
|
||||||
LocalID string // used by storage engine
|
LocalID string // used by storage engine
|
||||||
|
State string `json:"-"`
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,6 +187,18 @@ func (p *Profile) BlockPeer(onion string) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlockedPeers calculates a list of Peers who have been Blocked.
|
||||||
|
func (p *Profile) BlockedPeers() []string {
|
||||||
|
blockedPeers := []string{}
|
||||||
|
for _, contact := range p.GetContacts() {
|
||||||
|
c, _ := p.GetContact(contact)
|
||||||
|
if c.Blocked {
|
||||||
|
blockedPeers = append(blockedPeers, c.Onion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return blockedPeers
|
||||||
|
}
|
||||||
|
|
||||||
// TrustPeer sets a contact to trusted
|
// TrustPeer sets a contact to trusted
|
||||||
func (p *Profile) TrustPeer(onion string) (err error) {
|
func (p *Profile) TrustPeer(onion string) (err error) {
|
||||||
p.lock.Lock()
|
p.lock.Lock()
|
||||||
|
|
|
@ -5,11 +5,11 @@ import (
|
||||||
"cwtch.im/cwtch/model"
|
"cwtch.im/cwtch/model"
|
||||||
"cwtch.im/cwtch/protocol"
|
"cwtch.im/cwtch/protocol"
|
||||||
"cwtch.im/cwtch/protocol/connections"
|
"cwtch.im/cwtch/protocol/connections"
|
||||||
|
"encoding/base32"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -24,7 +24,6 @@ type cwtchPeer struct {
|
||||||
shutdown bool
|
shutdown bool
|
||||||
started bool
|
started bool
|
||||||
|
|
||||||
engine connections.Engine
|
|
||||||
queue *event.Queue
|
queue *event.Queue
|
||||||
eventBus *event.Manager
|
eventBus *event.Manager
|
||||||
}
|
}
|
||||||
|
@ -47,9 +46,7 @@ type CwtchPeer interface {
|
||||||
SendMessageToGroupTracked(string, string) (string, error)
|
SendMessageToGroupTracked(string, string) (string, error)
|
||||||
|
|
||||||
GetProfile() *model.Profile
|
GetProfile() *model.Profile
|
||||||
|
GetPeerState(string) connections.ConnectionState
|
||||||
GetPeers() map[string]connections.ConnectionState
|
|
||||||
GetServers() map[string]connections.ConnectionState
|
|
||||||
|
|
||||||
StartGroup(string) (string, []byte, error)
|
StartGroup(string) (string, []byte, error)
|
||||||
|
|
||||||
|
@ -57,13 +54,16 @@ type CwtchPeer interface {
|
||||||
ExportGroup(string) (string, error)
|
ExportGroup(string) (string, error)
|
||||||
|
|
||||||
GetGroup(string) *model.Group
|
GetGroup(string) *model.Group
|
||||||
|
GetGroupState(string) connections.ConnectionState
|
||||||
GetGroups() []string
|
GetGroups() []string
|
||||||
AddContact(nick, onion string, publickey []byte, trusted bool)
|
AddContact(nick, onion string, trusted bool)
|
||||||
GetContacts() []string
|
GetContacts() []string
|
||||||
GetContact(string) *model.PublicProfile
|
GetContact(string) *model.PublicProfile
|
||||||
|
|
||||||
IsStarted() bool
|
IsStarted() bool
|
||||||
Listen()
|
Listen()
|
||||||
|
StartPeersConnections()
|
||||||
|
StartGroupConnections()
|
||||||
Shutdown()
|
Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,19 +90,8 @@ func (cp *cwtchPeer) Init(acn connectivity.ACN, eventBus *event.Manager) {
|
||||||
cp.eventBus = eventBus
|
cp.eventBus = eventBus
|
||||||
cp.eventBus.Subscribe(event.EncryptedGroupMessage, cp.queue.EventChannel)
|
cp.eventBus.Subscribe(event.EncryptedGroupMessage, cp.queue.EventChannel)
|
||||||
cp.eventBus.Subscribe(event.NewGroupInvite, cp.queue.EventChannel)
|
cp.eventBus.Subscribe(event.NewGroupInvite, cp.queue.EventChannel)
|
||||||
|
cp.eventBus.Subscribe(event.ServerStateChange, cp.queue.EventChannel)
|
||||||
// Calculate a list of Peers who have been Blocked.
|
cp.eventBus.Subscribe(event.PeerStateChange, cp.queue.EventChannel)
|
||||||
blockedPeers := []string{}
|
|
||||||
for _, contact := range cp.Profile.GetContacts() {
|
|
||||||
c, _ := cp.Profile.GetContact(contact)
|
|
||||||
if c.Blocked {
|
|
||||||
blockedPeers = append(blockedPeers, c.Onion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Would be nice if ProtocolEngine did not need to explicitly be given the Private Key.
|
|
||||||
identity := identity.InitializeV3(cp.Profile.Name, &cp.Profile.Ed25519PrivateKey, &cp.Profile.Ed25519PublicKey)
|
|
||||||
cp.engine = connections.NewProtocolEngine(identity, cp.Profile.Ed25519PrivateKey, acn, eventBus, blockedPeers)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImportGroup intializes a group from an imported source rather than a peer invite
|
// ImportGroup intializes a group from an imported source rather than a peer invite
|
||||||
|
@ -175,8 +164,9 @@ func (cp *cwtchPeer) GetGroup(groupID string) *model.Group {
|
||||||
return cp.Profile.GetGroupByGroupID(groupID)
|
return cp.Profile.GetGroupByGroupID(groupID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cp *cwtchPeer) AddContact(nick, onion string, publickey []byte, trusted bool) {
|
func (cp *cwtchPeer) AddContact(nick, onion string, trusted bool) {
|
||||||
pp := &model.PublicProfile{Name: nick, Ed25519PublicKey: publickey, Trusted: trusted, Blocked: false, Onion: onion, Attributes: map[string]string{"nick": nick}}
|
decodedPub, _ := base32.StdEncoding.DecodeString(strings.ToUpper(onion))
|
||||||
|
pp := &model.PublicProfile{Name: nick, Ed25519PublicKey: decodedPub, Trusted: trusted, Blocked: false, Onion: onion, Attributes: map[string]string{"nick": nick}}
|
||||||
cp.Profile.AddContact(onion, pp)
|
cp.Profile.AddContact(onion, pp)
|
||||||
pd, _ := json.Marshal(pp)
|
pd, _ := json.Marshal(pp)
|
||||||
cp.eventBus.Publish(event.NewEvent(event.PeerCreated, map[event.Field]string{
|
cp.eventBus.Publish(event.NewEvent(event.PeerCreated, map[event.Field]string{
|
||||||
|
@ -201,6 +191,14 @@ func (cp *cwtchPeer) GetProfile() *model.Profile {
|
||||||
return cp.Profile
|
return cp.Profile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cp *cwtchPeer) GetPeerState(onion string) connections.ConnectionState {
|
||||||
|
return connections.ConnectionStateType[cp.Profile.Contacts[onion].State]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cp *cwtchPeer) GetGroupState(groupid string) connections.ConnectionState {
|
||||||
|
return connections.ConnectionStateType[cp.Profile.Groups[groupid].State]
|
||||||
|
}
|
||||||
|
|
||||||
// PeerWithOnion is the entry point for cwtchPeer relationships
|
// PeerWithOnion is the entry point for cwtchPeer relationships
|
||||||
func (cp *cwtchPeer) PeerWithOnion(onion string) *connections.PeerPeerConnection {
|
func (cp *cwtchPeer) PeerWithOnion(onion string) *connections.PeerPeerConnection {
|
||||||
cp.eventBus.Publish(event.NewEvent(event.PeerRequest, map[event.Field]string{event.RemotePeer: onion}))
|
cp.eventBus.Publish(event.NewEvent(event.PeerRequest, map[event.Field]string{event.RemotePeer: onion}))
|
||||||
|
@ -255,16 +253,6 @@ func (cp *cwtchPeer) SendMessageToPeer(onion string, message string) string {
|
||||||
return event.EventID
|
return event.EventID
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPeers returns a list of peer connections.
|
|
||||||
func (cp *cwtchPeer) GetPeers() map[string]connections.ConnectionState {
|
|
||||||
return cp.engine.GetPeers()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetServers returns a list of server connections
|
|
||||||
func (cp *cwtchPeer) GetServers() map[string]connections.ConnectionState {
|
|
||||||
return cp.engine.GetServers()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TrustPeer sets an existing peer relationship to trusted
|
// TrustPeer sets an existing peer relationship to trusted
|
||||||
func (cp *cwtchPeer) TrustPeer(peer string) error {
|
func (cp *cwtchPeer) TrustPeer(peer string) error {
|
||||||
err := cp.Profile.TrustPeer(peer)
|
err := cp.Profile.TrustPeer(peer)
|
||||||
|
@ -298,14 +286,34 @@ func (cp *cwtchPeer) RejectInvite(groupID string) {
|
||||||
cp.Profile.RejectInvite(groupID)
|
cp.Profile.RejectInvite(groupID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Listen makes the peer open a listening port to accept incoming connections (and be detactably online)
|
||||||
func (cp *cwtchPeer) Listen() {
|
func (cp *cwtchPeer) Listen() {
|
||||||
cp.eventBus.Publish(event.NewEvent(event.ProtocolEngineStartListen, map[event.Field]string{}))
|
cp.eventBus.Publish(event.NewEvent(event.ProtocolEngineStartListen, map[event.Field]string{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartGroupConnections attempts to connect to all group servers (thus initiating reconnect attempts in the conectionsmanager)
|
||||||
|
func (cp *cwtchPeer) StartPeersConnections() {
|
||||||
|
for _, contact := range cp.GetContacts() {
|
||||||
|
cp.PeerWithOnion(contact)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartPeerConnections attempts to connect to all peers (thus initiating reconnect attempts in the conectionsmanager)
|
||||||
|
func (cp *cwtchPeer) StartGroupConnections() {
|
||||||
|
joinedServers := map[string]bool{}
|
||||||
|
for _, groupID := range cp.GetGroups() {
|
||||||
|
// Only send a join server packet if we haven't joined this server yet...
|
||||||
|
group := cp.GetGroup(groupID)
|
||||||
|
if joined := joinedServers[groupID]; group.Accepted && !joined {
|
||||||
|
cp.JoinServer(group.GroupServer)
|
||||||
|
joinedServers[group.GroupServer] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Shutdown kills all connections and cleans up all goroutines for the peer
|
// Shutdown kills all connections and cleans up all goroutines for the peer
|
||||||
func (cp *cwtchPeer) Shutdown() {
|
func (cp *cwtchPeer) Shutdown() {
|
||||||
cp.shutdown = true
|
cp.shutdown = true
|
||||||
cp.engine.Shutdown()
|
|
||||||
cp.queue.Shutdown()
|
cp.queue.Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,6 +336,16 @@ func (cp *cwtchPeer) eventHandler() {
|
||||||
var groupInvite protocol.GroupChatInvite
|
var groupInvite protocol.GroupChatInvite
|
||||||
proto.Unmarshal([]byte(ev.Data[event.GroupInvite]), &groupInvite)
|
proto.Unmarshal([]byte(ev.Data[event.GroupInvite]), &groupInvite)
|
||||||
cp.Profile.ProcessInvite(&groupInvite, ev.Data[event.RemotePeer])
|
cp.Profile.ProcessInvite(&groupInvite, ev.Data[event.RemotePeer])
|
||||||
|
case event.PeerStateChange:
|
||||||
|
if _, exists := cp.Profile.Contacts[ev.Data[event.RemotePeer]]; exists {
|
||||||
|
cp.Profile.Contacts[ev.Data[event.RemotePeer]].State = ev.Data[event.ConnectionState]
|
||||||
|
}
|
||||||
|
case event.ServerStateChange:
|
||||||
|
for _, group := range cp.Profile.Groups {
|
||||||
|
if group.GroupServer == ev.Data[event.GroupServer] {
|
||||||
|
group.State = ev.Data[event.ConnectionState]
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
if ev.EventType != "" {
|
if ev.EventType != "" {
|
||||||
log.Errorf("peer event handler received an event it was not subscribed for: %v", ev.EventType)
|
log.Errorf("peer event handler received an event it was not subscribed for: %v", ev.EventType)
|
||||||
|
|
|
@ -42,12 +42,12 @@ func (m *Manager) ManagePeerConnection(host string, engine Engine) *PeerPeerConn
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManageServerConnection creates a new ServerConnection for Host with the given callback handler.
|
// ManageServerConnection creates a new ServerConnection for Host with the given callback handler.
|
||||||
func (m *Manager) ManageServerConnection(host string, messageHandler func(string, *protocol.GroupMessage), closedHandler func(string)) {
|
func (m *Manager) ManageServerConnection(host string, engine Engine, messageHandler func(string, *protocol.GroupMessage), closedHandler func(string)) {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
|
|
||||||
psc, exists := m.serverConnections[host]
|
psc, exists := m.serverConnections[host]
|
||||||
|
|
||||||
newPsc := NewPeerServerConnection(m.acn, host)
|
newPsc := NewPeerServerConnection(engine, host)
|
||||||
newPsc.GroupMessageHandler = messageHandler
|
newPsc.GroupMessageHandler = messageHandler
|
||||||
newPsc.CloseHandler = closedHandler
|
newPsc.CloseHandler = closedHandler
|
||||||
go newPsc.Run()
|
go newPsc.Run()
|
||||||
|
@ -60,28 +60,6 @@ func (m *Manager) ManageServerConnection(host string, messageHandler func(string
|
||||||
m.lock.Unlock()
|
m.lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPeers returns a map of all peer connections with their state
|
|
||||||
func (m *Manager) GetPeers() map[string]ConnectionState {
|
|
||||||
rm := make(map[string]ConnectionState)
|
|
||||||
m.lock.Lock()
|
|
||||||
for onion, ppc := range m.peerConnections {
|
|
||||||
rm[onion] = ppc.GetState()
|
|
||||||
}
|
|
||||||
m.lock.Unlock()
|
|
||||||
return rm
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetServers returns a map of all server connections with their state.
|
|
||||||
func (m *Manager) GetServers() map[string]ConnectionState {
|
|
||||||
rm := make(map[string]ConnectionState)
|
|
||||||
m.lock.Lock()
|
|
||||||
for onion, psc := range m.serverConnections {
|
|
||||||
rm[onion] = psc.GetState()
|
|
||||||
}
|
|
||||||
m.lock.Unlock()
|
|
||||||
return rm
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPeerPeerConnectionForOnion safely returns a given peer connection
|
// GetPeerPeerConnectionForOnion safely returns a given peer connection
|
||||||
func (m *Manager) GetPeerPeerConnectionForOnion(host string) (ppc *PeerPeerConnection) {
|
func (m *Manager) GetPeerPeerConnectionForOnion(host string) (ppc *PeerPeerConnection) {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
|
@ -103,7 +81,6 @@ func (m *Manager) AttemptReconnections() {
|
||||||
maxTimeout := time.Minute * 5
|
maxTimeout := time.Minute * 5
|
||||||
// nearly instant first run, next few runs will prolly be too quick to have any FAILED and will gracefully slow to MAX after that
|
// nearly instant first run, next few runs will prolly be too quick to have any FAILED and will gracefully slow to MAX after that
|
||||||
timeout := time.Millisecond * 500
|
timeout := time.Millisecond * 500
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-time.After(timeout):
|
case <-time.After(timeout):
|
||||||
|
|
|
@ -45,6 +45,7 @@ type engine struct {
|
||||||
type Engine interface {
|
type Engine interface {
|
||||||
Identity() identity.Identity
|
Identity() identity.Identity
|
||||||
ACN() connectivity.ACN
|
ACN() connectivity.ACN
|
||||||
|
EventManager() *event.Manager
|
||||||
|
|
||||||
GetPeerHandler(string) *CwtchPeerHandler
|
GetPeerHandler(string) *CwtchPeerHandler
|
||||||
ContactRequest(string, string) string
|
ContactRequest(string, string) string
|
||||||
|
@ -52,9 +53,6 @@ type Engine interface {
|
||||||
LookupContact(string, rsa.PublicKey) (bool, bool)
|
LookupContact(string, rsa.PublicKey) (bool, bool)
|
||||||
LookupContactV3(string, ed25519.PublicKey) (bool, bool)
|
LookupContactV3(string, ed25519.PublicKey) (bool, bool)
|
||||||
|
|
||||||
GetPeers() map[string]ConnectionState
|
|
||||||
GetServers() map[string]ConnectionState
|
|
||||||
|
|
||||||
Shutdown()
|
Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +92,10 @@ func (e *engine) Identity() identity.Identity {
|
||||||
return e.identity
|
return e.identity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *engine) EventManager() *event.Manager {
|
||||||
|
return e.eventManager
|
||||||
|
}
|
||||||
|
|
||||||
// eventHandler process events from other subsystems
|
// eventHandler process events from other subsystems
|
||||||
func (e *engine) eventHandler() {
|
func (e *engine) eventHandler() {
|
||||||
for {
|
for {
|
||||||
|
@ -244,7 +246,7 @@ func (e *engine) finishedFetch(server string) {
|
||||||
|
|
||||||
// joinServer manages a new server connection with the given onion address
|
// joinServer manages a new server connection with the given onion address
|
||||||
func (e *engine) joinServer(onion string) {
|
func (e *engine) joinServer(onion string) {
|
||||||
e.connectionsManager.ManageServerConnection(onion, e.receiveGroupMessage, e.finishedFetch)
|
e.connectionsManager.ManageServerConnection(onion, e, e.receiveGroupMessage, e.finishedFetch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendMessageToGroup attempts to sent the given message to the given group id.
|
// sendMessageToGroup attempts to sent the given message to the given group id.
|
||||||
|
@ -264,16 +266,6 @@ func (e *engine) sendMessageToGroup(server string, ct []byte, sig []byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPeers returns a list of peer connections.
|
|
||||||
func (e *engine) GetPeers() map[string]ConnectionState {
|
|
||||||
return e.connectionsManager.GetPeers()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetServers returns a list of server connections
|
|
||||||
func (e *engine) GetServers() map[string]ConnectionState {
|
|
||||||
return e.connectionsManager.GetServers()
|
|
||||||
}
|
|
||||||
|
|
||||||
// CwtchPeerInstance encapsulates incoming peer connections
|
// CwtchPeerInstance encapsulates incoming peer connections
|
||||||
type CwtchPeerInstance struct {
|
type CwtchPeerInstance struct {
|
||||||
rai *application.Instance
|
rai *application.Instance
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package connections
|
package connections
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"cwtch.im/cwtch/event"
|
||||||
"cwtch.im/cwtch/protocol/connections/peer"
|
"cwtch.im/cwtch/protocol/connections/peer"
|
||||||
"errors"
|
"errors"
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go"
|
"git.openprivacy.ca/openprivacy/libricochet-go"
|
||||||
|
@ -28,6 +29,14 @@ func NewPeerPeerConnection(peerhostname string, protocolEngine Engine) *PeerPeer
|
||||||
return ppc
|
return ppc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ppc *PeerPeerConnection) setState(state ConnectionState) {
|
||||||
|
ppc.state = state
|
||||||
|
ppc.protocolEngine.EventManager().Publish(event.NewEvent(event.PeerStateChange, map[event.Field]string{
|
||||||
|
event.RemotePeer: string(ppc.PeerHostname),
|
||||||
|
event.ConnectionState: ConnectionStateName[state],
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
// GetState returns the current connection state
|
// GetState returns the current connection state
|
||||||
func (ppc *PeerPeerConnection) GetState() ConnectionState {
|
func (ppc *PeerPeerConnection) GetState() ConnectionState {
|
||||||
return ppc.state
|
return ppc.state
|
||||||
|
@ -78,14 +87,14 @@ func (ppc *PeerPeerConnection) WaitTilAuthenticated() {
|
||||||
|
|
||||||
// Run manages the setup and teardown of a peer->peer connection
|
// Run manages the setup and teardown of a peer->peer connection
|
||||||
func (ppc *PeerPeerConnection) Run() error {
|
func (ppc *PeerPeerConnection) Run() error {
|
||||||
ppc.state = CONNECTING
|
ppc.setState(CONNECTING)
|
||||||
rc, err := goricochet.Open(ppc.protocolEngine.ACN(), ppc.PeerHostname)
|
rc, err := goricochet.Open(ppc.protocolEngine.ACN(), ppc.PeerHostname)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ppc.connection = rc
|
ppc.connection = rc
|
||||||
ppc.state = CONNECTED
|
ppc.setState(CONNECTED)
|
||||||
_, err := connection.HandleOutboundConnection(ppc.connection).ProcessAuthAsV3Client(ppc.protocolEngine.Identity())
|
_, err := connection.HandleOutboundConnection(ppc.connection).ProcessAuthAsV3Client(ppc.protocolEngine.Identity())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
ppc.state = AUTHENTICATED
|
ppc.setState(AUTHENTICATED)
|
||||||
go func() {
|
go func() {
|
||||||
ppc.connection.Do(func() error {
|
ppc.connection.Do(func() error {
|
||||||
ppc.connection.RequestOpenChannel("im.cwtch.peer", &peer.CwtchPeerChannel{Handler: ppc.protocolEngine.GetPeerHandler(ppc.PeerHostname)})
|
ppc.connection.RequestOpenChannel("im.cwtch.peer", &peer.CwtchPeerChannel{Handler: ppc.protocolEngine.GetPeerHandler(ppc.PeerHostname)})
|
||||||
|
@ -101,13 +110,13 @@ func (ppc *PeerPeerConnection) Run() error {
|
||||||
ppc.connection.Process(ppc)
|
ppc.connection.Process(ppc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ppc.state = FAILED
|
ppc.setState(FAILED)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the connection
|
// Close closes the connection
|
||||||
func (ppc *PeerPeerConnection) Close() {
|
func (ppc *PeerPeerConnection) Close() {
|
||||||
ppc.state = KILLED
|
ppc.setState(KILLED)
|
||||||
if ppc.connection != nil {
|
if ppc.connection != nil {
|
||||||
ppc.connection.Close()
|
ppc.connection.Close()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package connections
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"cwtch.im/cwtch/event"
|
||||||
"cwtch.im/cwtch/protocol"
|
"cwtch.im/cwtch/protocol"
|
||||||
"cwtch.im/cwtch/protocol/connections/fetch"
|
"cwtch.im/cwtch/protocol/connections/fetch"
|
||||||
"cwtch.im/cwtch/protocol/connections/listen"
|
"cwtch.im/cwtch/protocol/connections/listen"
|
||||||
|
@ -10,7 +11,6 @@ import (
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go"
|
"git.openprivacy.ca/openprivacy/libricochet-go"
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
|
"git.openprivacy.ca/openprivacy/libricochet-go/channels"
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
|
"git.openprivacy.ca/openprivacy/libricochet-go/connection"
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
|
||||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
|
@ -23,16 +23,16 @@ type PeerServerConnection struct {
|
||||||
Server string
|
Server string
|
||||||
state ConnectionState
|
state ConnectionState
|
||||||
connection *connection.Connection
|
connection *connection.Connection
|
||||||
acn connectivity.ACN
|
protocolEngine Engine
|
||||||
|
|
||||||
GroupMessageHandler func(string, *protocol.GroupMessage)
|
GroupMessageHandler func(string, *protocol.GroupMessage)
|
||||||
CloseHandler func(string)
|
CloseHandler func(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPeerServerConnection creates a new Peer->Server outbound connection
|
// NewPeerServerConnection creates a new Peer->Server outbound connection
|
||||||
func NewPeerServerConnection(acn connectivity.ACN, serverhostname string) *PeerServerConnection {
|
func NewPeerServerConnection(engine Engine, serverhostname string) *PeerServerConnection {
|
||||||
psc := new(PeerServerConnection)
|
psc := new(PeerServerConnection)
|
||||||
psc.acn = acn
|
psc.protocolEngine = engine
|
||||||
psc.Server = serverhostname
|
psc.Server = serverhostname
|
||||||
psc.Init()
|
psc.Init()
|
||||||
return psc
|
return psc
|
||||||
|
@ -43,6 +43,14 @@ func (psc *PeerServerConnection) GetState() ConnectionState {
|
||||||
return psc.state
|
return psc.state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (psc *PeerServerConnection) setState(state ConnectionState) {
|
||||||
|
psc.state = state
|
||||||
|
psc.protocolEngine.EventManager().Publish(event.NewEvent(event.ServerStateChange, map[event.Field]string{
|
||||||
|
event.GroupServer: string(psc.Server),
|
||||||
|
event.ConnectionState: ConnectionStateName[state],
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
// WaitTilAuthenticated waits until the underlying connection is authenticated
|
// WaitTilAuthenticated waits until the underlying connection is authenticated
|
||||||
func (psc *PeerServerConnection) WaitTilAuthenticated() {
|
func (psc *PeerServerConnection) WaitTilAuthenticated() {
|
||||||
for {
|
for {
|
||||||
|
@ -56,15 +64,15 @@ func (psc *PeerServerConnection) WaitTilAuthenticated() {
|
||||||
// Run manages the setup and teardown of a peer server connection
|
// Run manages the setup and teardown of a peer server connection
|
||||||
func (psc *PeerServerConnection) Run() error {
|
func (psc *PeerServerConnection) Run() error {
|
||||||
log.Infof("Connecting to %v", psc.Server)
|
log.Infof("Connecting to %v", psc.Server)
|
||||||
rc, err := goricochet.Open(psc.acn, psc.Server)
|
rc, err := goricochet.Open(psc.protocolEngine.ACN(), psc.Server)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
psc.connection = rc
|
psc.connection = rc
|
||||||
psc.state = CONNECTED
|
psc.setState(CONNECTED)
|
||||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
_, err := connection.HandleOutboundConnection(psc.connection).ProcessAuthAsV3Client(identity.InitializeV3("cwtchpeer", &priv, &pub))
|
_, err := connection.HandleOutboundConnection(psc.connection).ProcessAuthAsV3Client(identity.InitializeV3("cwtchpeer", &priv, &pub))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
psc.state = AUTHENTICATED
|
psc.setState(AUTHENTICATED)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
psc.connection.Do(func() error {
|
psc.connection.Do(func() error {
|
||||||
|
@ -81,7 +89,7 @@ func (psc *PeerServerConnection) Run() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
psc.state = FAILED
|
psc.setState(FAILED)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +136,7 @@ func (psc *PeerServerConnection) SendGroupMessage(gm *protocol.GroupMessage) err
|
||||||
|
|
||||||
// Close shuts down the connection (freeing the handler goroutines)
|
// Close shuts down the connection (freeing the handler goroutines)
|
||||||
func (psc *PeerServerConnection) Close() {
|
func (psc *PeerServerConnection) Close() {
|
||||||
psc.state = KILLED
|
psc.setState(KILLED)
|
||||||
if psc.connection != nil {
|
if psc.connection != nil {
|
||||||
psc.connection.Close()
|
psc.connection.Close()
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package connections
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
"cwtch.im/cwtch/event"
|
||||||
"cwtch.im/cwtch/protocol"
|
"cwtch.im/cwtch/protocol"
|
||||||
"cwtch.im/cwtch/server/fetch"
|
"cwtch.im/cwtch/server/fetch"
|
||||||
"cwtch.im/cwtch/server/send"
|
"cwtch.im/cwtch/server/send"
|
||||||
|
@ -75,7 +76,11 @@ func TestPeerServerConnection(t *testing.T) {
|
||||||
<-listenChan
|
<-listenChan
|
||||||
onionAddr := identity.Hostname()
|
onionAddr := identity.Hostname()
|
||||||
|
|
||||||
psc := NewPeerServerConnection(connectivity.LocalProvider(), "127.0.0.1:5451|"+onionAddr)
|
manager := &event.Manager{}
|
||||||
|
manager.Initialize()
|
||||||
|
engine := NewProtocolEngine(identity, priv, connectivity.LocalProvider(), manager, nil)
|
||||||
|
|
||||||
|
psc := NewPeerServerConnection(engine, "127.0.0.1:5451|"+onionAddr)
|
||||||
numcalls := 0
|
numcalls := 0
|
||||||
psc.GroupMessageHandler = func(s string, gm *protocol.GroupMessage) {
|
psc.GroupMessageHandler = func(s string, gm *protocol.GroupMessage) {
|
||||||
numcalls++
|
numcalls++
|
||||||
|
|
|
@ -7,7 +7,7 @@ type ConnectionState int
|
||||||
// DISCONNECTED - No existing connection has been made, or all attempts have failed
|
// DISCONNECTED - No existing connection has been made, or all attempts have failed
|
||||||
// CONNECTING - We are in the process of attempting to connect to a given endpoint
|
// CONNECTING - We are in the process of attempting to connect to a given endpoint
|
||||||
// CONNECTED - We have connected but not yet authenticated
|
// CONNECTED - We have connected but not yet authenticated
|
||||||
// AUTHENTICATED - im.ricochet.auth-hidden-server has succeeded on thec onnection.
|
// AUTHENTICATED - im.ricochet.auth-hidden-server has succeeded on the connection.
|
||||||
const (
|
const (
|
||||||
DISCONNECTED ConnectionState = iota
|
DISCONNECTED ConnectionState = iota
|
||||||
CONNECTING
|
CONNECTING
|
||||||
|
@ -20,4 +20,8 @@ const (
|
||||||
var (
|
var (
|
||||||
// ConnectionStateName allows conversion of states to their string representations
|
// ConnectionStateName allows conversion of states to their string representations
|
||||||
ConnectionStateName = []string{"Disconnected", "Connecting", "Connected", "Authenticated", "Failed", "Killed"}
|
ConnectionStateName = []string{"Disconnected", "Connecting", "Connected", "Authenticated", "Failed", "Killed"}
|
||||||
|
|
||||||
|
// ConnectionStateType allows conversion of strings to their state type
|
||||||
|
ConnectionStateType = map[string]ConnectionState{"Disconnected": DISCONNECTED, "Connecting": CONNECTING,
|
||||||
|
"Connected": CONNECTED, "Authenticated": AUTHENTICATED, "Failed": FAILED, "Killed": KILLED}
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,6 +21,7 @@ type profileStore struct {
|
||||||
profile *model.Profile
|
profile *model.Profile
|
||||||
eventManager *event.Manager
|
eventManager *event.Manager
|
||||||
queue *event.Queue
|
queue *event.Queue
|
||||||
|
writer bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProfileStore is an interface to managing the storage of Cwtch Profiles
|
// ProfileStore is an interface to managing the storage of Cwtch Profiles
|
||||||
|
@ -30,11 +31,11 @@ type ProfileStore interface {
|
||||||
GetProfileCopy() *model.Profile
|
GetProfileCopy() *model.Profile
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProfileStore returns a profile store backed by a filestore listening for events and saving them
|
// NewProfileWriterStore returns a profile store backed by a filestore listening for events and saving them
|
||||||
// directory should be $appDir/profiles/$rand
|
// directory should be $appDir/profiles/$rand
|
||||||
func NewProfileStore(eventManager *event.Manager, directory, password string, profile *model.Profile) ProfileStore {
|
func NewProfileWriterStore(eventManager *event.Manager, directory, password string, profile *model.Profile) ProfileStore {
|
||||||
os.Mkdir(directory, 0700)
|
os.Mkdir(directory, 0700)
|
||||||
ps := &profileStore{fs: NewFileStore(directory, profileFilename, password), password: password, directory: directory, profile: profile, eventManager: eventManager, streamStores: map[string]StreamStore{}}
|
ps := &profileStore{fs: NewFileStore(directory, profileFilename, password), password: password, directory: directory, profile: profile, eventManager: eventManager, streamStores: map[string]StreamStore{}, writer: true}
|
||||||
ps.queue = event.NewEventQueue(100)
|
ps.queue = event.NewEventQueue(100)
|
||||||
if profile != nil {
|
if profile != nil {
|
||||||
ps.save()
|
ps.save()
|
||||||
|
@ -55,6 +56,15 @@ func NewProfileStore(eventManager *event.Manager, directory, password string, pr
|
||||||
return ps
|
return ps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewProfileReaderStore returns a profile store backed by a filestore
|
||||||
|
// directory should be $appDir/profiles/$rand
|
||||||
|
func NewProfileReaderStore(directory, password string, profile *model.Profile) ProfileStore {
|
||||||
|
os.Mkdir(directory, 0700)
|
||||||
|
ps := &profileStore{fs: NewFileStore(directory, profileFilename, password), password: password, directory: directory, profile: profile, eventManager: nil, streamStores: map[string]StreamStore{}, writer: true}
|
||||||
|
|
||||||
|
return ps
|
||||||
|
}
|
||||||
|
|
||||||
// NewProfile creates a new profile for use in the profile store.
|
// NewProfile creates a new profile for use in the profile store.
|
||||||
func NewProfile(name string) *model.Profile {
|
func NewProfile(name string) *model.Profile {
|
||||||
profile := model.GenerateNewProfile(name)
|
profile := model.GenerateNewProfile(name)
|
||||||
|
@ -62,8 +72,11 @@ func NewProfile(name string) *model.Profile {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *profileStore) save() error {
|
func (ps *profileStore) save() error {
|
||||||
|
if ps.writer {
|
||||||
bytes, _ := json.Marshal(ps.profile)
|
bytes, _ := json.Marshal(ps.profile)
|
||||||
return ps.fs.Save(bytes)
|
return ps.fs.Save(bytes)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load instantiates a cwtchPeer from the file store
|
// Load instantiates a cwtchPeer from the file store
|
||||||
|
@ -181,5 +194,7 @@ func (ps *profileStore) eventHandler() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ps *profileStore) Shutdown() {
|
func (ps *profileStore) Shutdown() {
|
||||||
|
if ps.queue != nil {
|
||||||
ps.queue.Shutdown()
|
ps.queue.Shutdown()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ func TestProfileStoreWriteRead(t *testing.T) {
|
||||||
eventBus := new(event.Manager)
|
eventBus := new(event.Manager)
|
||||||
eventBus.Initialize()
|
eventBus.Initialize()
|
||||||
profile := NewProfile(testProfileName)
|
profile := NewProfile(testProfileName)
|
||||||
ps1 := NewProfileStore(eventBus, testingDir, password, profile)
|
ps1 := NewProfileWriterStore(eventBus, testingDir, password, profile)
|
||||||
|
|
||||||
eventBus.Publish(event.NewEvent(event.SetAttribute, map[event.Field]string{event.Key: testKey, event.Data: testVal}))
|
eventBus.Publish(event.NewEvent(event.SetAttribute, map[event.Field]string{event.Key: testKey, event.Data: testVal}))
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
|
@ -50,7 +50,7 @@ func TestProfileStoreWriteRead(t *testing.T) {
|
||||||
|
|
||||||
ps1.Shutdown()
|
ps1.Shutdown()
|
||||||
|
|
||||||
ps2 := NewProfileStore(eventBus, testingDir, password, nil)
|
ps2 := NewProfileWriterStore(eventBus, testingDir, password, nil)
|
||||||
err = ps2.Load()
|
err = ps2.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error createing profileStore: %v\n", err)
|
t.Errorf("Error createing profileStore: %v\n", err)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package testing
|
package testing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cwtch.im/cwtch/event"
|
app2 "cwtch.im/cwtch/app"
|
||||||
"cwtch.im/cwtch/model"
|
"cwtch.im/cwtch/model"
|
||||||
"cwtch.im/cwtch/peer"
|
"cwtch.im/cwtch/peer"
|
||||||
"cwtch.im/cwtch/protocol/connections"
|
"cwtch.im/cwtch/protocol/connections"
|
||||||
|
@ -55,16 +55,17 @@ func serverCheck(t *testing.T, serverAddr string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForPeerServerConnection(t *testing.T, peer peer.CwtchPeer, server string) {
|
func waitForPeerGroupConnection(t *testing.T, peer peer.CwtchPeer, groupID string) {
|
||||||
for {
|
for {
|
||||||
servers := peer.GetServers()
|
_, ok := peer.GetProfile().Groups[groupID]
|
||||||
state, ok := servers[server]
|
|
||||||
if ok {
|
if ok {
|
||||||
|
state := peer.GetGroupState(groupID)
|
||||||
|
//log.Infof("Waiting for Peer %v to join group %v - state: %v\n", peer.GetProfile().Name, groupID, state)
|
||||||
if state == connections.FAILED {
|
if state == connections.FAILED {
|
||||||
t.Fatalf("%v could not connect to %v", peer.GetProfile().Onion, server)
|
t.Fatalf("%v could not connect to %v", peer.GetProfile().Onion, groupID)
|
||||||
}
|
}
|
||||||
if state != connections.AUTHENTICATED {
|
if state != connections.AUTHENTICATED {
|
||||||
fmt.Printf("peer %v waiting connect to server %v, currently: %v\n", peer.GetProfile().Onion, server, connections.ConnectionStateName[state])
|
fmt.Printf("peer %v waiting connect to group %v, currently: %v\n", peer.GetProfile().Onion, groupID, connections.ConnectionStateName[state])
|
||||||
time.Sleep(time.Second * 5)
|
time.Sleep(time.Second * 5)
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
|
@ -78,9 +79,11 @@ func waitForPeerServerConnection(t *testing.T, peer peer.CwtchPeer, server strin
|
||||||
|
|
||||||
func waitForPeerPeerConnection(t *testing.T, peera peer.CwtchPeer, peerb peer.CwtchPeer) {
|
func waitForPeerPeerConnection(t *testing.T, peera peer.CwtchPeer, peerb peer.CwtchPeer) {
|
||||||
for {
|
for {
|
||||||
peers := peera.GetPeers()
|
//peers := peera.GetPeers()
|
||||||
state, ok := peers[peerb.GetProfile().Onion]
|
_, ok := peera.GetProfile().Contacts[peerb.GetProfile().Onion]
|
||||||
if ok {
|
if ok {
|
||||||
|
state := peera.GetPeerState(peerb.GetProfile().Onion)
|
||||||
|
//log.Infof("Waiting for Peer %v to peer with peer: %v - state: %v\n", peera.GetProfile().Name, peerb.GetProfile().Name, state)
|
||||||
if state == connections.FAILED {
|
if state == connections.FAILED {
|
||||||
t.Fatalf("%v could not connect to %v", peera.GetProfile().Onion, peerb.GetProfile().Onion)
|
t.Fatalf("%v could not connect to %v", peera.GetProfile().Onion, peerb.GetProfile().Onion)
|
||||||
}
|
}
|
||||||
|
@ -132,35 +135,33 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
||||||
|
|
||||||
numGoRoutinesPostServer := runtime.NumGoroutine()
|
numGoRoutinesPostServer := runtime.NumGoroutine()
|
||||||
|
|
||||||
|
app := app2.NewApp(acn, "./storage")
|
||||||
|
|
||||||
// ***** cwtchPeer setup *****
|
// ***** cwtchPeer setup *****
|
||||||
|
|
||||||
// It's important that each Peer have their own EventBus
|
// It's important that each Peer have their own EventBus
|
||||||
aliceEventBus := new(event.Manager)
|
/*aliceEventBus := new(event.Manager)
|
||||||
aliceEventBus.Initialize()
|
aliceEventBus.Initialize()
|
||||||
bobEventBus := new(event.Manager)
|
bobEventBus := new(event.Manager)
|
||||||
bobEventBus.Initialize()
|
bobEventBus.Initialize()
|
||||||
carolEventBus := new(event.Manager)
|
carolEventBus := new(event.Manager)
|
||||||
carolEventBus.Initialize()
|
carolEventBus.Initialize()*/
|
||||||
|
|
||||||
fmt.Println("Creating Alice...")
|
fmt.Println("Creating Alice...")
|
||||||
alice := peer.NewCwtchPeer("Alice")
|
alice, _ := app.CreatePeer("alice", "asdfasdf")
|
||||||
alice.Init(acn, aliceEventBus)
|
|
||||||
alice.Listen()
|
|
||||||
fmt.Println("Alice created:", alice.GetProfile().Onion)
|
fmt.Println("Alice created:", alice.GetProfile().Onion)
|
||||||
|
|
||||||
fmt.Println("Creating Bob...")
|
fmt.Println("Creating Bob...")
|
||||||
bob := peer.NewCwtchPeer("Bob")
|
bob, _ := app.CreatePeer("bob", "asdfasdf")
|
||||||
bob.Init(acn, bobEventBus)
|
|
||||||
bob.Listen()
|
|
||||||
fmt.Println("Bob created:", bob.GetProfile().Onion)
|
fmt.Println("Bob created:", bob.GetProfile().Onion)
|
||||||
|
|
||||||
fmt.Println("Creating Carol...")
|
fmt.Println("Creating Carol...")
|
||||||
carol := peer.NewCwtchPeer("Carol")
|
carol, _ := app.CreatePeer("Carol", "asdfasdf")
|
||||||
carol.Init(acn, carolEventBus)
|
|
||||||
carol.Listen()
|
|
||||||
fmt.Println("Carol created:", carol.GetProfile().Onion)
|
fmt.Println("Carol created:", carol.GetProfile().Onion)
|
||||||
|
|
||||||
fmt.Println("Waiting for Alice, Bob, and Carol to connection with onion network...")
|
app.LaunchPeers()
|
||||||
|
|
||||||
|
fmt.Println("Waiting for Alice, Bob, and Carol to connect with onion network...")
|
||||||
time.Sleep(time.Second * 90)
|
time.Sleep(time.Second * 90)
|
||||||
numGoRoutinesPostPeerStart := runtime.NumGoroutine()
|
numGoRoutinesPostPeerStart := runtime.NumGoroutine()
|
||||||
|
|
||||||
|
@ -175,8 +176,10 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Alice peering with Bob...")
|
fmt.Println("Alice peering with Bob...")
|
||||||
|
alice.AddContact("Bob", bob.GetProfile().Onion, false) // Add contact so we can track connection state
|
||||||
alice.PeerWithOnion(bob.GetProfile().Onion)
|
alice.PeerWithOnion(bob.GetProfile().Onion)
|
||||||
fmt.Println("Alice peering with Carol...")
|
fmt.Println("Alice peering with Carol...")
|
||||||
|
alice.AddContact("Carol", carol.GetProfile().Onion, false)
|
||||||
alice.PeerWithOnion(carol.GetProfile().Onion)
|
alice.PeerWithOnion(carol.GetProfile().Onion)
|
||||||
|
|
||||||
fmt.Println("Alice joining server...")
|
fmt.Println("Alice joining server...")
|
||||||
|
@ -185,10 +188,10 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
||||||
bob.JoinServer(serverAddr)
|
bob.JoinServer(serverAddr)
|
||||||
|
|
||||||
fmt.Println("Waiting for alice to join server...")
|
fmt.Println("Waiting for alice to join server...")
|
||||||
waitForPeerServerConnection(t, alice, serverAddr)
|
waitForPeerGroupConnection(t, alice, groupID)
|
||||||
|
|
||||||
fmt.Println("Waiting for bob to join server...")
|
//fmt.Println("Waiting for bob to join server...")
|
||||||
waitForPeerServerConnection(t, bob, serverAddr)
|
//waitForPeerGroupConnection(t, bob, groupID)
|
||||||
|
|
||||||
fmt.Println("Waiting for alice and Bob to peer...")
|
fmt.Println("Waiting for alice and Bob to peer...")
|
||||||
waitForPeerPeerConnection(t, alice, bob)
|
waitForPeerPeerConnection(t, alice, bob)
|
||||||
|
@ -217,35 +220,9 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
||||||
|
|
||||||
numGoRoutinesPostServerConnect := runtime.NumGoroutine()
|
numGoRoutinesPostServerConnect := runtime.NumGoroutine()
|
||||||
|
|
||||||
// ***** Fill up message history of server ******
|
|
||||||
|
|
||||||
/*
|
|
||||||
// filler group will be used to fill up the servers message history a bit to stress test fetch later for carol
|
|
||||||
fillerGroupId, _, err := alice.Profile.StartGroup(serverAddr)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("Failed to init filler group: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Alice filling message history of server...")
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
|
|
||||||
go func (x int) {
|
|
||||||
time.Sleep(time.Second * time.Duration(x))
|
|
||||||
err := alice.SendMessageToGroup(fillerGroupId, aliceLines[0])
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("SEND", x, "ERROR:", err)
|
|
||||||
} else {
|
|
||||||
fmt.Println("SEND", x, " SUCCESS!")
|
|
||||||
}
|
|
||||||
}(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Second * 110)
|
|
||||||
*/
|
|
||||||
// Wait for them to join the server
|
// Wait for them to join the server
|
||||||
waitForPeerServerConnection(t, alice, serverAddr)
|
waitForPeerGroupConnection(t, alice, groupID)
|
||||||
waitForPeerServerConnection(t, bob, serverAddr)
|
waitForPeerGroupConnection(t, bob, groupID)
|
||||||
//numGouRoutinesPostServerConnect := runtime.NumGoroutine()
|
//numGouRoutinesPostServerConnect := runtime.NumGoroutine()
|
||||||
|
|
||||||
// ***** Conversation *****
|
// ***** Conversation *****
|
||||||
|
@ -291,14 +268,13 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Shutting down Alice...")
|
fmt.Println("Shutting down Alice...")
|
||||||
alice.Shutdown()
|
app.ShutdownPeer(alice.GetProfile().Onion)
|
||||||
aliceEventBus.Shutdown()
|
|
||||||
time.Sleep(time.Second * 5)
|
time.Sleep(time.Second * 5)
|
||||||
numGoRoutinesPostAlice := runtime.NumGoroutine()
|
numGoRoutinesPostAlice := runtime.NumGoroutine()
|
||||||
|
|
||||||
fmt.Println("Carol joining server...")
|
fmt.Println("Carol joining server...")
|
||||||
carol.JoinServer(serverAddr)
|
carol.JoinServer(serverAddr)
|
||||||
waitForPeerServerConnection(t, carol, serverAddr)
|
waitForPeerGroupConnection(t, carol, groupID)
|
||||||
numGoRotinesPostCarolConnect := runtime.NumGoroutine()
|
numGoRotinesPostCarolConnect := runtime.NumGoroutine()
|
||||||
|
|
||||||
fmt.Println("Bob> ", bobLines[2])
|
fmt.Println("Bob> ", bobLines[2])
|
||||||
|
@ -380,8 +356,7 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Shutting down Bob...")
|
fmt.Println("Shutting down Bob...")
|
||||||
bob.Shutdown()
|
app.ShutdownPeer(bob.GetProfile().Onion)
|
||||||
bobEventBus.Shutdown()
|
|
||||||
time.Sleep(time.Second * 3)
|
time.Sleep(time.Second * 3)
|
||||||
numGoRoutinesPostBob := runtime.NumGoroutine()
|
numGoRoutinesPostBob := runtime.NumGoroutine()
|
||||||
if server != nil {
|
if server != nil {
|
||||||
|
@ -392,8 +367,7 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
||||||
numGoRoutinesPostServerShutdown := runtime.NumGoroutine()
|
numGoRoutinesPostServerShutdown := runtime.NumGoroutine()
|
||||||
|
|
||||||
fmt.Println("Shutting down Carol...")
|
fmt.Println("Shutting down Carol...")
|
||||||
carol.Shutdown()
|
app.ShutdownPeer(carol.GetProfile().Onion)
|
||||||
carolEventBus.Shutdown()
|
|
||||||
time.Sleep(time.Second * 3)
|
time.Sleep(time.Second * 3)
|
||||||
numGoRoutinesPostCarol := runtime.NumGoroutine()
|
numGoRoutinesPostCarol := runtime.NumGoroutine()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue