Refactoring Application to remove channel handler duplications

Also simplfies application a lot, still not complete, but i like this approach much more
This commit is contained in:
Sarah Jamie Lewis 2018-01-07 16:51:46 -08:00
parent be62634c46
commit 1a2fb40d91
5 changed files with 108 additions and 141 deletions

View File

@ -3,98 +3,28 @@ package application
import (
"crypto/rsa"
"github.com/s-rah/go-ricochet"
"github.com/s-rah/go-ricochet/channels"
"github.com/s-rah/go-ricochet/connection"
"github.com/s-rah/go-ricochet/identity"
"log"
"net"
"sync"
"time"
)
// RicochetApplication bundles many useful constructs that are
// likely standard in a ricochet application
type RicochetApplication struct {
contactManager ContactManagerInterface
privateKey *rsa.PrivateKey
chatMessageHandler func(*RicochetApplicationInstance, uint32, time.Time, string)
chatMessageAckHandler func(*RicochetApplicationInstance, uint32)
onConnected func(*RicochetApplicationInstance)
onLeave func(*RicochetApplicationInstance)
l net.Listener
instances []*RicochetApplicationInstance
lock sync.Mutex
contactManager ContactManagerInterface
privateKey *rsa.PrivateKey
l net.Listener
instances []*ApplicationInstance
lock sync.Mutex
aif ApplicationInstanceFactory
}
type RicochetApplicationInstance struct {
connection.AutoConnectionHandler
connection *connection.Connection
RemoteHostname string
ChatMessageHandler func(*RicochetApplicationInstance, uint32, time.Time, string)
ChatMessageAckHandler func(*RicochetApplicationInstance, uint32)
OnLeave func(*RicochetApplicationInstance)
}
func (rai *RicochetApplicationInstance) ContactRequest(name string, message string) string {
return "Accepted"
}
func (rai *RicochetApplicationInstance) ContactRequestRejected() {
}
func (rai *RicochetApplicationInstance) ContactRequestAccepted() {
}
func (rai *RicochetApplicationInstance) ContactRequestError() {
}
func (rai *RicochetApplicationInstance) SendChatMessage(message string) {
rai.connection.Do(func() error {
// Technically this errors afte the second time but we can ignore it.
rai.connection.RequestOpenChannel("im.ricochet.chat",
&channels.ChatChannel{
Handler: rai,
})
channel := rai.connection.Channel("im.ricochet.chat", channels.Outbound)
if channel != nil {
chatchannel, ok := channel.Handler.(*channels.ChatChannel)
if ok {
chatchannel.SendMessage(message)
}
}
return nil
})
}
func (rai *RicochetApplicationInstance) ChatMessage(messageID uint32, when time.Time, message string) bool {
go rai.ChatMessageHandler(rai, messageID, when, message)
return true
}
func (rai *RicochetApplicationInstance) ChatMessageAck(messageID uint32, accepted bool) {
rai.ChatMessageAckHandler(rai, messageID)
}
func (ra *RicochetApplication) Init(pk *rsa.PrivateKey, cm ContactManagerInterface) {
func (ra *RicochetApplication) Init(pk *rsa.PrivateKey, af ApplicationInstanceFactory, cm ContactManagerInterface) {
ra.privateKey = pk
ra.aif = af
ra.contactManager = cm
ra.chatMessageHandler = func(*RicochetApplicationInstance, uint32, time.Time, string) {}
ra.chatMessageAckHandler = func(*RicochetApplicationInstance, uint32) {}
}
func (ra *RicochetApplication) OnChatMessage(call func(*RicochetApplicationInstance, uint32, time.Time, string)) {
ra.chatMessageHandler = call
}
func (ra *RicochetApplication) OnChatMessageAck(call func(*RicochetApplicationInstance, uint32)) {
ra.chatMessageAckHandler = call
}
func (ra *RicochetApplication) OnConnected(call func(*RicochetApplicationInstance)) {
ra.onConnected = call
}
func (ra *RicochetApplication) OnLeave(call func(*RicochetApplicationInstance)) {
ra.onLeave = call
}
func (ra *RicochetApplication) handleConnection(conn net.Conn) {
@ -114,39 +44,17 @@ func (ra *RicochetApplication) handleConnection(conn net.Conn) {
return
}
rc.TraceLog(true)
rai := new(RicochetApplicationInstance)
rai.Init()
rai.RemoteHostname = rc.RemoteHostname
rai.connection = rc
rai.ChatMessageHandler = ra.chatMessageHandler
rai.ChatMessageAckHandler = ra.chatMessageAckHandler
rai.OnLeave = ra.onLeave
rai.RegisterChannelHandler("im.ricochet.contact.request", func() channels.Handler {
contact := new(channels.ContactRequestChannel)
contact.Handler = rai
return contact
})
rai.RegisterChannelHandler("im.ricochet.chat", func() channels.Handler {
chat := new(channels.ChatChannel)
chat.Handler = rai
return chat
})
rai := ra.aif.GetApplicationInstance(rc)
ra.lock.Lock()
ra.instances = append(ra.instances, rai)
ra.lock.Unlock()
go ra.onConnected(rai)
rc.Process(rai)
}
func (rai *RicochetApplicationInstance) OnClosed(err error) {
rai.OnLeave(rai)
}
func (ra *RicochetApplication) Broadcast(message string) {
func (ra *RicochetApplication) Broadcast(do func(rai *ApplicationInstance)) {
ra.lock.Lock()
for _, rai := range ra.instances {
rai.SendChatMessage(message)
do(rai)
}
ra.lock.Unlock()
}

View File

@ -0,0 +1,40 @@
package application
import (
"github.com/s-rah/go-ricochet/channels"
"github.com/s-rah/go-ricochet/connection"
)
// A concrete instance of a ricochet application, encapsulating a connection
type ApplicationInstance struct {
connection.AutoConnectionHandler
Connection *connection.Connection
RemoteHostname string
}
// Application instance factory
type ApplicationInstanceFactory struct {
handlerMap map[string]func(*ApplicationInstance) func() channels.Handler
}
// Init setsup an Application Factory
func (af *ApplicationInstanceFactory) Init() {
af.handlerMap = make(map[string]func(*ApplicationInstance) func() channels.Handler)
}
// AddHandler
func (af *ApplicationInstanceFactory) AddHandler(ctype string, chandler func(*ApplicationInstance) func() channels.Handler) {
af.handlerMap[ctype] = chandler
}
// GetApplicationInstance,
func (af *ApplicationInstanceFactory) GetApplicationInstance(rc *connection.Connection) *ApplicationInstance {
rai := new(ApplicationInstance)
rai.Init()
rai.RemoteHostname = rc.RemoteHostname
rai.Connection = rc
for t, h := range af.handlerMap {
rai.RegisterChannelHandler(t, h(rai))
}
return rai
}

View File

@ -1,31 +0,0 @@
package main
import (
"github.com/s-rah/go-ricochet/application"
"github.com/s-rah/go-ricochet/utils"
"log"
"time"
)
func main() {
commandbot := new(application.RicochetApplication)
pk, err := utils.LoadPrivateKeyFromFile("./testing/private_key")
if err != nil {
log.Fatalf("error reading private key file: %v", err)
}
l, err := application.SetupOnion("127.0.0.1:9051", "", pk, 9878)
if err != nil {
log.Fatalf("error setting up onion service: %v", err)
}
commandbot.Init(pk, new(application.AcceptAllContactManager))
commandbot.OnChatMessage(func(rai *application.RicochetApplicationInstance, id uint32, timestamp time.Time, message string) {
log.Printf("message from %v - %v", rai.RemoteHostname, message)
go echobot.Broadcast(rai.RemoteHostname + " " + message)
})
log.Printf("commandbot listening on %s", l.Addr().String())
commandbot.Run(l)
}

View File

@ -2,11 +2,53 @@ package main
import (
"github.com/s-rah/go-ricochet/application"
"github.com/s-rah/go-ricochet/channels"
"github.com/s-rah/go-ricochet/utils"
"log"
"time"
)
type EchoBotInstance struct {
rai *application.ApplicationInstance
ra *application.RicochetApplication
}
func (ebi *EchoBotInstance) Init(rai *application.ApplicationInstance, ra *application.RicochetApplication) {
ebi.rai = rai
ebi.ra = ra
}
func (ebi *EchoBotInstance) ChatMessage(messageID uint32, when time.Time, message string) bool {
log.Printf("message from %v - %v", ebi.rai.RemoteHostname, message)
go ebi.ra.Broadcast(func(rai *application.ApplicationInstance) {
ebi.SendChatMessage(rai, ebi.rai.RemoteHostname+" "+message)
})
return true
}
func (ebi *EchoBotInstance) ChatMessageAck(messageID uint32, accepted bool) {
}
func (ebi *EchoBotInstance) SendChatMessage(rai *application.ApplicationInstance, message string) {
rai.Connection.Do(func() error {
// Technically this errors after the second time but we can ignore it.
rai.Connection.RequestOpenChannel("im.ricochet.chat",
&channels.ChatChannel{
Handler: ebi,
})
channel := rai.Connection.Channel("im.ricochet.chat", channels.Outbound)
if channel != nil {
chatchannel, ok := channel.Handler.(*channels.ChatChannel)
if ok {
chatchannel.SendMessage(message)
}
}
return nil
})
}
func main() {
echobot := new(application.RicochetApplication)
pk, err := utils.LoadPrivateKeyFromFile("./testing/private_key")
@ -21,11 +63,19 @@ func main() {
log.Fatalf("error setting up onion service: %v", err)
}
echobot.Init(pk, new(application.AcceptAllContactManager))
echobot.OnChatMessage(func(rai *application.RicochetApplicationInstance, id uint32, timestamp time.Time, message string) {
log.Printf("message from %v - %v", rai.RemoteHostname, message)
go echobot.Broadcast(rai.RemoteHostname + " " + message)
af := application.ApplicationInstanceFactory{}
af.Init()
af.AddHandler("im.ricochet.chat", func(rai *application.ApplicationInstance) func() channels.Handler {
ebi := new(EchoBotInstance)
ebi.Init(rai, echobot)
return func() channels.Handler {
chat := new(channels.ChatChannel)
chat.Handler = ebi
return chat
}
})
echobot.Init(pk, af, new(application.AcceptAllContactManager))
log.Printf("echobot listening on %s", l.Addr().String())
echobot.Run(l)
}

View File

@ -7,11 +7,11 @@ import (
)
const (
// InvalidPacketLengthError is returned whenever ricochet receives a packet too small or too large to conform to the spec.
// InvalidPacketLengthError is returned whenever ricochet receives a packet too small or too large to conform to the spec.
InvalidPacketLengthError = Error("InvalidPacketLengthError")
// InvalidChannelIDError channels must be between 0 and 65535
InvalidChannelIDError = Error("InvalidChannelIDError")
InvalidChannelIDError = Error("InvalidChannelIDError")
)
// RicochetData is a structure containing the raw data and the channel it the