Fixing up Server APIs #330
|
@ -297,6 +297,6 @@ const (
|
|||
|
||||
// Bool strings
|
||||
const (
|
||||
True = "true"
|
||||
True = "true"
|
||||
False = "false"
|
||||
)
|
2
go.sum
2
go.sum
|
@ -1,5 +1,7 @@
|
|||
cwtch.im/tapir v0.2.0 h1:7MkoR5+uEuPW34/O0GZRidnIjq/01Cfm8nl5IRuqpGc=
|
||||
cwtch.im/tapir v0.2.0/go.mod h1:xzzZ28adyUXNkYL1YodcHsAiTt3IJ8Loc29YVn9mIEQ=
|
||||
cwtch.im/tapir v0.2.1 h1:t1YJB9q5sV1A9xwiiwL6WVfw3dwQWLoecunuzT1PQtw=
|
||||
cwtch.im/tapir v0.2.1/go.mod h1:xzzZ28adyUXNkYL1YodcHsAiTt3IJ8Loc29YVn9mIEQ=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.3 h1:PSHUmNqaW7BZUX8n2eTDeNbjsuRe+t5Ae0Og+P+jDM0=
|
||||
git.openprivacy.ca/openprivacy/bine v0.0.3/go.mod h1:13ZqhKyqakDsN/ZkQkIGNULsmLyqtXc46XBcnuXm/mU=
|
||||
git.openprivacy.ca/openprivacy/connectivity v1.2.0 h1:dbZ5CRl11vg3BNHdzRKSlDP8OUtDB+mf6FkxMVf73qw=
|
||||
|
|
|
@ -262,9 +262,20 @@ func (e *engine) peerWithOnion(onion string) {
|
|||
// peerWithTokenServer is the entry point for cwtchPeer - server relationships
|
||||
// needs to be run in a goroutine as will block on Open.
|
||||
func (e *engine) peerWithTokenServer(onion string, tokenServerOnion string, tokenServerY string) {
|
||||
|
||||
service, exists := e.ephemeralServices.Load(onion)
|
||||
if exists {
|
||||
connection := service.(*tor.BaseOnionService)
|
||||
if conn, err := connection.GetConnection(onion); err == nil {
|
||||
if conn.IsClosed() == false {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Otherwise...let's reconnect
|
||||
}
|
||||
|
||||
log.Debugf("Peering with Token Server %v %v", onion, tokenServerOnion)
|
||||
e.ignoreOnShutdown(e.serverConnecting)(onion)
|
||||
|
||||
// Create a new ephemeral service for this connection
|
||||
ephemeralService := new(tor.BaseOnionService)
|
||||
eid, epk := primitives.InitializeEphemeralIdentity()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"cwtch.im/cwtch/model"
|
||||
cwtchserver "cwtch.im/cwtch/server"
|
||||
"cwtch.im/tapir/primitives"
|
||||
|
@ -9,7 +10,6 @@ import (
|
|||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
mrand "math/rand"
|
||||
"crypto/rand"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
@ -45,7 +45,7 @@ func main() {
|
|||
|
||||
// we don't need real randomness for the port, just to avoid a possible conflict...
|
||||
mrand.Seed(int64(time.Now().Nanosecond()))
|
||||
controlPort := mrand.Intn(1000)+9052
|
||||
controlPort := mrand.Intn(1000) + 9052
|
||||
|
||||
// generate a random password
|
||||
key := make([]byte, 64)
|
||||
|
@ -54,7 +54,7 @@ func main() {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
os.MkdirAll("tordir/tor",0700)
|
||||
os.MkdirAll("tordir/tor", 0700)
|
||||
tor.NewTorrc().WithHashedPassword(base64.StdEncoding.EncodeToString(key)).WithControlPort(controlPort).Build("./tordir/tor/torrc")
|
||||
acn, err := tor.NewTorACNWithAuth("tordir", "", controlPort, tor.HashedPasswordAuthenticator{Password: base64.StdEncoding.EncodeToString(key)})
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
type counter struct {
|
||||
startTime time.Time
|
||||
count uint64
|
||||
total uint64
|
||||
}
|
||||
|
||||
// Counter providers a threadsafe counter to use for storing long running counts
|
||||
|
@ -25,7 +26,7 @@ type Counter interface {
|
|||
|
||||
// NewCounter initializes a counter starting at time.Now() and a count of 0 and returns it
|
||||
func NewCounter() Counter {
|
||||
c := &counter{startTime: time.Now(), count: 0}
|
||||
c := &counter{startTime: time.Now(), count: 0, total: 0}
|
||||
return c
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ const (
|
|||
// Monitors is a package of metrics for a Cwtch Server including message count, CPU, Mem, and conns
|
||||
type Monitors struct {
|
||||
MessageCounter Counter
|
||||
TotalMessageCounter Counter
|
||||
Messages MonitorHistory
|
||||
CPU MonitorHistory
|
||||
Memory MonitorHistory
|
||||
|
@ -36,7 +37,11 @@ func (mp *Monitors) Start(ts tapir.Service, configDir string, log bool) {
|
|||
mp.starttime = time.Now()
|
||||
mp.breakChannel = make(chan bool)
|
||||
mp.MessageCounter = NewCounter()
|
||||
mp.Messages = NewMonitorHistory(Count, Cumulative, func() (c float64) { c = float64(mp.MessageCounter.Count()); mp.MessageCounter.Reset(); return })
|
||||
|
||||
// Maintain a count of total messages
|
||||
mp.TotalMessageCounter = NewCounter()
|
||||
mp.Messages = NewMonitorHistory(Count, Cumulative, func() (c float64) { c = float64(mp.MessageCounter.Count()); mp.TotalMessageCounter.Add(int(c)); mp.MessageCounter.Reset(); return })
|
||||
|
||||
var pidUsageLock sync.Mutex
|
||||
mp.CPU = NewMonitorHistory(Percent, Average, func() float64 {
|
||||
pidUsageLock.Lock()
|
||||
|
|
110
server/server.go
110
server/server.go
|
@ -11,45 +11,46 @@ import (
|
|||
"cwtch.im/tapir/persistence"
|
||||
"cwtch.im/tapir/primitives"
|
||||
"cwtch.im/tapir/primitives/privacypass"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/connectivity"
|
||||
"git.openprivacy.ca/openprivacy/connectivity/tor"
|
||||
"git.openprivacy.ca/openprivacy/log"
|
||||
"os"
|
||||
"time"
|
||||
"path"
|
||||
)
|
||||
|
||||
// Server encapsulates a complete, compliant Cwtch server.
|
||||
type Server struct {
|
||||
service tapir.Service
|
||||
config Config
|
||||
metricsPack metrics.Monitors
|
||||
closed bool
|
||||
tokenTapirService tapir.Service
|
||||
tokenServer *privacypass.TokenServer
|
||||
tokenService primitives.Identity
|
||||
tokenServicePrivKey ed25519.PrivateKey
|
||||
service tapir.Service
|
||||
config Config
|
||||
metricsPack metrics.Monitors
|
||||
tokenTapirService tapir.Service
|
||||
tokenServer *privacypass.TokenServer
|
||||
tokenService primitives.Identity
|
||||
tokenServicePrivKey ed25519.PrivateKey
|
||||
tokenServiceStopped bool
|
||||
onionServiceStopped bool
|
||||
running bool
|
||||
existingMessageCount int
|
||||
}
|
||||
|
||||
// Setup initialized a server from a given configuration
|
||||
func (s *Server) Setup(serverConfig Config) {
|
||||
s.config = serverConfig
|
||||
bs := new(persistence.BoltPersistence)
|
||||
bs.Open("./tokens.db")
|
||||
bs.Open(path.Join(serverConfig.ConfigDir, "tokens.db"))
|
||||
s.tokenServer = privacypass.NewTokenServerFromStore(bs)
|
||||
s.tokenService = s.config.TokenServiceIdentity()
|
||||
s.tokenServicePrivKey = s.config.TokenServerPrivateKey
|
||||
}
|
||||
|
||||
// Identity returns the main onion identity of the server
|
||||
func (s *Server) Identity() primitives.Identity {
|
||||
return s.config.Identity()
|
||||
}
|
||||
|
||||
// Run starts a server with the given privateKey
|
||||
// TODO: surface errors
|
||||
// TODO: handle HUP/KILL signals to exit and close Tor gracefully
|
||||
// TODO: handle user input to exit
|
||||
func (s *Server) Run(acn connectivity.ACN) {
|
||||
s.closed = false
|
||||
|
||||
func (s *Server) Run(acn connectivity.ACN) error {
|
||||
addressIdentity := tor.GetTorV3Hostname(s.config.PublicKey)
|
||||
|
||||
//tokenService := privacypass.NewTokenServer()
|
||||
identity := primitives.InitializeIdentity("", &s.config.PrivateKey, &s.config.PublicKey)
|
||||
var service tapir.Service
|
||||
service = new(tor2.BaseOnionService)
|
||||
|
@ -61,29 +62,30 @@ func (s *Server) Run(acn connectivity.ACN) {
|
|||
ms := new(storage.MessageStore)
|
||||
err := ms.Init(s.config.ConfigDir, s.config.MaxBufferLines, s.metricsPack.MessageCounter)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
acn.Close()
|
||||
os.Exit(1)
|
||||
return err
|
||||
}
|
||||
|
||||
// Needed because we only collect metrics on a per-session basis
|
||||
// TODO fix metrics so they persist across sessions?
|
||||
s.existingMessageCount = len(ms.FetchMessages())
|
||||
|
||||
s.tokenTapirService = new(tor2.BaseOnionService)
|
||||
s.tokenTapirService.Init(acn, s.tokenServicePrivKey, &s.tokenService)
|
||||
tokenApplication := new(applications.TokenApplication)
|
||||
tokenApplication.TokenService = s.tokenServer
|
||||
powTokenApp := new(applications.ApplicationChain).
|
||||
ChainApplication(new(applications.ProofOfWorkApplication), applications.SuccessfulProofOfWorkCapability).
|
||||
ChainApplication(tokenApplication, applications.HasTokensCapability)
|
||||
go func() {
|
||||
s.tokenTapirService = new(tor2.BaseOnionService)
|
||||
s.tokenTapirService.Init(acn, s.tokenServicePrivKey, &s.tokenService)
|
||||
tokenApplication := new(applications.TokenApplication)
|
||||
tokenApplication.TokenService = s.tokenServer
|
||||
powTokenApp := new(applications.ApplicationChain).
|
||||
ChainApplication(new(applications.ProofOfWorkApplication), applications.SuccessfulProofOfWorkCapability).
|
||||
ChainApplication(tokenApplication, applications.HasTokensCapability)
|
||||
s.tokenTapirService.Listen(powTokenApp)
|
||||
s.tokenServiceStopped = true
|
||||
}()
|
||||
|
||||
for true {
|
||||
go func() {
|
||||
s.service.Listen(NewTokenBoardServer(ms, s.tokenServer))
|
||||
if s.closed {
|
||||
return
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
s.onionServiceStopped = true
|
||||
}()
|
||||
s.running = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// KeyBundle provides the signed keybundle of the server
|
||||
|
@ -97,10 +99,44 @@ func (s *Server) KeyBundle() *model.KeyBundle {
|
|||
return kb
|
||||
}
|
||||
|
||||
// CheckStatus returns true if the server is running and/or an error if any part of the server needs to be restarted.
|
||||
func (s *Server) CheckStatus() (bool, error) {
|
||||
|
||||
if s.onionServiceStopped == true || s.tokenServiceStopped == true {
|
||||
return s.running, fmt.Errorf("one of more server components are down: onion:%v token service: %v", s.onionServiceStopped, s.tokenServiceStopped)
|
||||
}
|
||||
return s.running, nil
|
||||
}
|
||||
|
||||
// Shutdown kills the app closing all connections and freeing all goroutines
|
||||
func (s *Server) Shutdown() {
|
||||
s.closed = true
|
||||
s.service.Shutdown()
|
||||
s.tokenTapirService.Shutdown()
|
||||
s.metricsPack.Stop()
|
||||
s.running = false
|
||||
}
|
||||
|
||||
// Statistics is an encapsulation of information about the server that an operator might want to know at a glance.
|
||||
type Statistics struct {
|
||||
TotalMessages int
|
||||
}
|
||||
|
||||
// GetStatistics is a stub method for providing some high level information about
|
||||
// the server operation to bundling applications (e.g. the UI)
|
||||
func (s *Server) GetStatistics() Statistics {
|
||||
// TODO Statistics from Metrics is very awkward. Metrics needs an overhaul to make safe
|
||||
total := s.existingMessageCount
|
||||
if s.metricsPack.TotalMessageCounter != nil {
|
||||
total += s.metricsPack.TotalMessageCounter.Count()
|
||||
}
|
||||
|
||||
return Statistics{
|
||||
TotalMessages: total,
|
||||
}
|
||||
}
|
||||
|
||||
// ConfigureAutostart sets whether this server should autostart (in the Cwtch UI/bundling application)
|
||||
func (s *Server) ConfigureAutostart(autostart bool) {
|
||||
s.config.AutoStart = autostart
|
||||
s.config.Save(s.config.ConfigDir, s.config.FilePath)
|
||||
}
|
||||
|
|
|
@ -19,12 +19,14 @@ type Reporting struct {
|
|||
// Config is a struct for storing basic server configuration
|
||||
type Config struct {
|
||||
ConfigDir string `json:"-"`
|
||||
FilePath string `json:"-"`
|
||||
MaxBufferLines int `json:"maxBufferLines"`
|
||||
PublicKey ed25519.PublicKey `json:"publicKey"`
|
||||
PrivateKey ed25519.PrivateKey `json:"privateKey"`
|
||||
TokenServerPublicKey ed25519.PublicKey `json:"tokenServerPublicKey"`
|
||||
TokenServerPrivateKey ed25519.PrivateKey `json:"tokenServerPrivateKey"`
|
||||
ServerReporting Reporting `json:"serverReporting"`
|
||||
AutoStart bool `json:"autostart"`
|
||||
}
|
||||
|
||||
// Identity returns an encapsulation of the servers keys
|
||||
|
@ -61,6 +63,9 @@ func LoadConfig(configDir, filename string) Config {
|
|||
ReportingGroupID: "",
|
||||
ReportingServerAddr: "",
|
||||
}
|
||||
config.AutoStart = false
|
||||
config.ConfigDir = configDir
|
||||
config.FilePath = filename
|
||||
|
||||
raw, err := ioutil.ReadFile(path.Join(configDir, filename))
|
||||
if err == nil {
|
||||
|
|
|
@ -139,7 +139,7 @@ func TestCwtchPeerIntegration(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Could not start Tor: %v", err)
|
||||
}
|
||||
pid, err := acn.GetPID()
|
||||
pid, _ := acn.GetPID()
|
||||
t.Logf("Tor pid: %v", pid)
|
||||
|
||||
// ***** Cwtch Server management *****
|
||||
|
|
Loading…
Reference in New Issue