diff --git a/.gitignore b/.gitignore index 36d5d76..1f5bce6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,12 +3,19 @@ !**/*.pem !**/*.dot !**/*.svg +!**/*.sh +!**/*.yaml !**/ +!./test/* +!**/.vscode/* +!**/doc/*.md +!**/doc/img/*.png !/.gitignore !/go.mod !/go.sum !/LICENSE !/README.md +!/Dockerfile vendor diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..7fbd18b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + // Utilisez IntelliSense pour en savoir plus sur les attributs possibles. + // Pointez pour afficher la description des attributs existants. + // Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to Process", + "type": "go", + "request": "attach", + "mode": "local", + "processId": "${command:pickGoProcess}" + }, + { + "name": "Launch Package", + "type": "go", + "preLaunchTask": "DeleteFolder", + "request": "launch", + "mode": "auto", + "program": "./test", + "args": [ + "4", + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..425df7e --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,84 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "MultiProcesses_prep", + "type": "shell", + "command": "./test/multiProcesses_prep.sh", + "problemMatcher": [] + }, + { + "label": "DeleteFolder", + "type": "shell", + "command": "rm -rf ~/.shoset", + "problemMatcher": [] + }, + { + "label": "RunMultiProcesses", + "runOptions": {}, + "dependsOrder": "sequence", + "dependsOn": [ + "MultiProcesses_prep", + "RunMultiProcesses_run" + ], + "problemMatcher": [] + }, + { + "label": "RunMultiProcesses_run", + "runOptions": {}, + "dependsOrder": "parallel", + "dependsOn": [ + "RunMultiProcesses_E", + "RunMultiProcesses_D", + "RunMultiProcesses_C", + "RunMultiProcesses_B", + "RunMultiProcesses_A" + ], + "problemMatcher": [ + "$go" + ] + }, + { + "label": "RunMultiProcesses_A", + "type": "shell", + "command": "./test/multiProcesses_A.sh", + "presentation": { + "group": "test" + } + }, + { + "label": "RunMultiProcesses_B", + "type": "shell", + "command": "./test/multiProcesses_B.sh", + "presentation": { + "group": "test" + } + }, + { + "label": "RunMultiProcesses_C", + "type": "shell", + "command": "./test/multiProcesses_C.sh", + "presentation": { + "group": "test" + } + }, + { + "label": "RunMultiProcesses_D", + "type": "shell", + "command": "./test/multiProcesses_D.sh", + "presentation": { + "group": "test" + } + }, + { + "label": "RunMultiProcesses_E", + "type": "shell", + "command": "./test/multiProcesses_E.sh", + "presentation": { + "group": "test" + } + } + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..570eef7 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +FROM ubuntu:latest as setup-build-stage-git +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y git +# temporary until merge to master +RUN git clone -b new_pki https://github.com/ditrit/shoset.git + +FROM golang:1.18-bullseye as setup-build-stage-shoset +WORKDIR /app +COPY --from=setup-build-stage-git . /app +WORKDIR /app/shoset +RUN go get +WORKDIR /app/shoset/test +RUN go build -o shoset + +FROM ubuntu:21.04 as setup-stage-production +COPY --from=setup-build-stage-shoset /app/shoset/test/shoset usr/bin/ +RUN apt-get update + +FROM setup-stage-production as stage-production +WORKDIR /usr/bin +CMD [ "./shoset", "4" ] diff --git a/README.md b/README.md index f6262b4..87bfe8a 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,42 @@ -# shoset +# Shoset + + +## Description + +Shoset is a smart Socket library, **resilient**, **distributed** and with **high availability**. + +**Resilient :** automatic reconnection and no startup order (As long as every shoset is certified.) . + +**Distributed :** with the brother system, any **shoset** can fail with no disruption to the network if it is not alone in its logical name. + +**High availability :** Built in redundancy. + +Send and receive data in the form of messages between **shosets**. -A smart multi-ends socket library. ## Documentation -Missing. -- Find a usage example [in the gandalf project](https://github.com/ditrit/gandalf-core/blob/master/aggregator/aggregator.go). +See full documentation [guide.md](doc/guide.md).
-## Design principles +## Install and Run -For now, over to the [team's taiga](https://taiga.orness.com/project/xavier-namt/wiki/shoset) +This project is written in **Golang**.
+You need to install **Golang** to run this project.
+See [Golang installation](https://golang.org/doc/install) for more information.
-## Running tests +### Install -```txt -git clone https://github.com/ditrit/shoset -go run test/test.go +```bash +go get github.com/ditrit/shoset ``` + +### Run + +For now, few tests are available.
+You can see them in the [guide.md](doc/guide.md) file.
+ + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details + diff --git a/certs/cert.pem b/certs/cert.pem deleted file mode 100644 index ecbf9b5..0000000 --- a/certs/cert.pem +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICHDCCAaKgAwIBAgIUSApEm90UcioAuQltssO/jNqUn9wwCgYIKoZIzj0EAwIw -RTELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUtU3RhdGUxDzANBgNVBAoMBk9y -bmVzczEQMA4GA1UEAwwHZ2FuZGFsZjAeFw0xOTEyMDQwOTA5NDhaFw0yOTEyMDEw -OTA5NDhaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMQ8wDQYD -VQQKDAZPcm5lc3MxEDAOBgNVBAMMB2dhbmRhbGYwdjAQBgcqhkjOPQIBBgUrgQQA -IgNiAATYjS0r7vtQ82KgrMENzJNrTBh/QZiKCxoaFzkc5afSwIk6aViZ3cGZazzg -7gOkbRNJCMCRY/mQd1hGzRG1OqRTJWr4fSbQzu8vTLltdGktKTraoWr3Eofc2rJO -8epYi0mjUzBRMB0GA1UdDgQWBBQnSqizDskOjYX8oUpBR3vBF/BzMzAfBgNVHSME -GDAWgBQnSqizDskOjYX8oUpBR3vBF/BzMzAPBgNVHRMBAf8EBTADAQH/MAoGCCqG -SM49BAMCA2gAMGUCMBmst+uBAkGZ8oRqpzxYQAigT1AFAuAyk++9LPIAekBCkyMa -HpPBt6ijGdhWJiitPQIxALfmD5FLDpDClKcR3a8MnRmcKkDCir3zOURdQk85hn/4 -WqCuKmWw9M7bYT1kJlAdxw== ------END CERTIFICATE----- diff --git a/certs/key.pem b/certs/key.pem deleted file mode 100644 index 27532b1..0000000 --- a/certs/key.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN EC PARAMETERS----- -BgUrgQQAIg== ------END EC PARAMETERS----- ------BEGIN EC PRIVATE KEY----- -MIGkAgEBBDBqtz8VxEp40pMtIj5Dt/tBAqCMbX738rJgpSEFxZqNJrB2SIGNmmcp -9H+ya7A/aX2gBwYFK4EEACKhZANiAATYjS0r7vtQ82KgrMENzJNrTBh/QZiKCxoa -Fzkc5afSwIk6aViZ3cGZazzg7gOkbRNJCMCRY/mQd1hGzRG1OqRTJWr4fSbQzu8v -TLltdGktKTraoWr3Eofc2rJO8epYi0k= ------END EC PRIVATE KEY----- diff --git a/commandFuncs.go b/commandFuncs.go deleted file mode 100644 index 5333f69..0000000 --- a/commandFuncs.go +++ /dev/null @@ -1,62 +0,0 @@ -package shoset - -import ( - "fmt" - "time" - - "github.com/ditrit/shoset/msg" -) - -// GetCommand : -func GetCommand(c *ShosetConn) (msg.Message, error) { - var cmd msg.Command - err := c.ReadMessage(&cmd) - return cmd, err -} - -// HandleCommand : -func HandleCommand(c *ShosetConn, message msg.Message) error { - cmd := message.(msg.Command) - c.GetCh().Queue["cmd"].Push(cmd, c.GetRemoteShosetType(), c.GetLocalAddress()) - return nil -} - -// SendCommand : -func SendCommand(c *Shoset, cmd msg.Message) { - fmt.Print("Sending Command.\n") - c.ConnsByName.IterateAll( - func(key string, conn *ShosetConn) { - conn.SendMessage(cmd) - }, - ) -} - -// WaitCommand : -func WaitCommand(c *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { - commandName, ok := args["name"] - if !ok { - return nil - } - term := make(chan *msg.Message, 1) - cont := true - go func() { - for cont { - message := replies.Get().GetMessage() - if message != nil { - command := message.(msg.Command) - if command.GetCommand() == commandName { - term <- &message - } - } else { - time.Sleep(time.Duration(10) * time.Millisecond) - } - } - }() - select { - case res := <-term: - cont = false - return res - case <-time.After(time.Duration(timeout) * time.Second): - return nil - } -} diff --git a/concurent_data/concurentSlice.go b/concurent_data/concurentSlice.go new file mode 100644 index 0000000..2fea204 --- /dev/null +++ b/concurent_data/concurentSlice.go @@ -0,0 +1,106 @@ +package concurentData + +import ( + "errors" + "fmt" + "sync" + "time" + + eventBus "github.com/ditrit/shoset/event_bus" +) + +// This is a thread safe slice that allows you to wait for the next change or for it to be empty. +type ConcurentSlice struct { + sliceValues []string + eventBus eventBus.EventBus // topics : change, empty (value not used) + m sync.RWMutex +} + +func NewConcurentSlice() ConcurentSlice { + return ConcurentSlice{eventBus: eventBus.NewEventBus()} +} + +// Appends data to the slice and publishes the change event. +func (cSlice *ConcurentSlice) AppendToConcurentSlice(data string) { + cSlice.m.Lock() + defer cSlice.m.Unlock() + + cSlice.sliceValues = append(cSlice.sliceValues, data) + cSlice.eventBus.Publish("change", true) +} + +// Deletes data from the slice and publishes the change (and empty) event. +func (cSlice *ConcurentSlice) DeleteFromConcurentSlice(data string) { + cSlice.m.Lock() + defer cSlice.m.Unlock() + + for i, a := range cSlice.sliceValues { + if a == data { + // Deletes data from the slice in an efficient way (avoids moving remaining data). + cSlice.sliceValues[i] = cSlice.sliceValues[len(cSlice.sliceValues)-1] + cSlice.sliceValues = cSlice.sliceValues[:len(cSlice.sliceValues)-1] + + cSlice.eventBus.Publish("change", true) + + if len(cSlice.sliceValues) == 0 { + cSlice.eventBus.Publish("empty", true) + } + return + } + } +} + +// Waits for the Slice to be empty +func (cSlice *ConcurentSlice) WaitForEmpty(timeout int) error { + cSlice.m.RLock() + if len(cSlice.sliceValues) != 0 { + // Subscribes a channel to the empty topic : + chEmpty := make(chan interface{}) + cSlice.eventBus.Subscribe("empty", chEmpty) + defer cSlice.eventBus.UnSubscribe("empty", chEmpty) + + cSlice.m.RUnlock() + select { + case <-chEmpty: + return nil + case <-time.After(time.Duration(timeout) * time.Second): + return errors.New("the list is no empty (timeout)") + } + } + cSlice.m.RUnlock() + return nil +} + +// Wait for the Slice to change +func (cSlice *ConcurentSlice) WaitForChange(timeout int) error { + cSlice.m.RLock() + + // Subscribes a channel to the change topic : + chChange := make(chan interface{}) + cSlice.eventBus.Subscribe("change", chChange) + defer cSlice.eventBus.UnSubscribe("change", chChange) + + cSlice.m.RUnlock() + select { + case <-chChange: + return nil + case <-time.After(time.Duration(timeout) * time.Second): + return errors.New("the list did not change (timeout)") + } +} + +func (cSlice *ConcurentSlice) Contains(data string) bool { + // contains range through a slice to search for a particular string + for _, v := range cSlice.sliceValues { + if v == data { + return true + } + } + return false +} + +func (cSlice *ConcurentSlice) String() string { + cSlice.m.RLock() + defer cSlice.m.RUnlock() + return fmt.Sprint(cSlice.sliceValues) +} diff --git a/concurent_data/concurentSlice_test.go b/concurent_data/concurentSlice_test.go new file mode 100644 index 0000000..b74b18f --- /dev/null +++ b/concurent_data/concurentSlice_test.go @@ -0,0 +1,84 @@ +package concurentData + +import ( + "fmt" + "sync" + "testing" + "time" +) + +func Test_concurentSlice(t *testing.T) { + cSlice := NewConcurentSlice() + + number := 10 //Changing this requires updating the strings the result is compared to. + + go func() { + for { + cSlice.WaitForChange(5) + fmt.Println("Change received.") + } + }() + + time.Sleep(10 * time.Millisecond) + + for i := 0; i < number; i++ { + cSlice.AppendToConcurentSlice("Test " + fmt.Sprint(i)) + } + if !cSlice.Contains("Test 0") { + t.Errorf("Wrong content after appending.") + } + if cSlice.String() != "[Test 0 Test 1 Test 2 Test 3 Test 4 Test 5 Test 6 Test 7 Test 8 Test 9]" { + t.Errorf("Wrong content after appending.") + } + fmt.Println(cSlice.String()) + + for i := 0; i < number-(number/2); i++ { + cSlice.DeleteFromConcurentSlice("Test " + fmt.Sprint(i)) + } + if cSlice.String() != "[Test 9 Test 8 Test 7 Test 6 Test 5]" { + t.Errorf("Wrong content after Clearing.") + } + fmt.Println(cSlice.String()) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < number-(number/2); i++ { + cSlice.AppendToConcurentSlice("Test " + fmt.Sprint(i)) + } + }() + wg.Add(1) + go func() { + defer wg.Done() + for i := 5; i < number; i++ { + cSlice.DeleteFromConcurentSlice("Test " + fmt.Sprint(i)) + } + }() + + wg.Wait() + + go func() { + for i := 0; i < number-(number/2); i++ { + cSlice.DeleteFromConcurentSlice("Test " + fmt.Sprint(i)) + fmt.Println(cSlice.String()) + time.Sleep(10 * time.Millisecond) + } + }() + + cSlice.WaitForEmpty(5) + + if cSlice.String() != "[]" { + t.Errorf("Wrong content after Clearing.") + } + + fmt.Println(cSlice.String()) + + cSlice.AppendToConcurentSlice("TEST") + + err := cSlice.WaitForEmpty(5) + + if err == nil { + t.Errorf("Timeout error failed.") + } +} diff --git a/config.go b/config.go new file mode 100644 index 0000000..919acb9 --- /dev/null +++ b/config.go @@ -0,0 +1,119 @@ +package shoset + +import ( + "os" + "path/filepath" + "sync" + + "github.com/rs/zerolog/log" + "github.com/spf13/viper" +) + +// Config: collection of configuration information for a shoset. +type Config struct { + baseDirectory string + fileName string + viper *viper.Viper + mu sync.Mutex +} + +// NewConfig returns a *Config object. +// Initialize home directory and viper. +func NewConfig(name string) *Config { + homeDirectory := "." + homeDirectory, err := os.UserHomeDir() + if err != nil { + log.Error().Msg("couldn't get home directory folder: " + err.Error()) + return nil + } + cfg := &Config{ + baseDirectory: filepath.Join(homeDirectory, ".shoset", name), + viper: viper.New(), + } + if err := mkdir(cfg.baseDirectory); err != nil { + log.Error().Msg("couldn't create folder: " + err.Error()) + return nil + } + return cfg +} + +// GetBaseDirectory returns baseDirectory from config, baseDirectory corresponds to homeDirectory + shoset repertory. +func (cfg *Config) GetBaseDirectory() string { return cfg.baseDirectory } + +// GetFileName returns fileName from config. +func (cfg *Config) GetFileName() string { return cfg.fileName } + +// SetFileName sets fileName for a config. +func (cfg *Config) SetFileName(fileName string) { cfg.fileName = fileName } + +// InitFolders creates following config folders if needed: //{cert, config}. +func (cfg *Config) InitFolders(name string) (string, error) { + if err := mkdir(filepath.Join(cfg.baseDirectory, name)); err != nil { + return VOID, err + } + if err := mkdir(filepath.Join(cfg.baseDirectory, name, PATH_CONFIG_DIRECTORY)); err != nil { + return VOID, err + } + if err := mkdir(filepath.Join(cfg.baseDirectory, name, PATH_CERT_DIRECTORY)); err != nil { + return VOID, err + } + return cfg.baseDirectory, nil +} + +// ReadConfig will load the configuration file from disk for a given fileName. +// Initialize viper parameters before reading. +func (cfg *Config) ReadConfig(fileName string) error { + + cfg.viper.AddConfigPath(filepath.Join(cfg.baseDirectory, fileName, PATH_CONFIG_DIRECTORY)) + cfg.viper.SetConfigName(CONFIG_FILE) + cfg.viper.SetConfigType(CONFIG_TYPE) + + err := cfg.viper.ReadInConfig() + + return err +} + +// WriteConfig writes current configuration to a given shoset. +func (cfg *Config) WriteConfig(fileName string) error { + cfg.mu.Lock() + defer cfg.mu.Unlock() + + return cfg.viper.WriteConfigAs(filepath.Join(cfg.baseDirectory, fileName, PATH_CONFIG_DIRECTORY, CONFIG_FILE)) +} + +// AppendToKey sets the value for a key for viper config. +func (cfg *Config) AppendToKey(key string, values []string) { + cfg.mu.Lock() + defer cfg.mu.Unlock() + + // Avoid duplicate + valueSlice := cfg.GetSlice(key) + for _, a := range values { + if !contains(valueSlice, a) { + valueSlice = append(valueSlice, a) + } + } + cfg.viper.Set(key, valueSlice) + cfg.viper.WriteConfig() +} + +// DeleteFromKey deletes the values from the list from the key in the viper config. +func (cfg *Config) DeleteFromKey(key string, value []string) { + cfg.mu.Lock() + defer cfg.mu.Unlock() + + valueCfg := cfg.GetSlice(key) + valueCfgOut := []string{} + for _, a := range valueCfg { + if !contains(value, a) { + valueCfgOut = append(valueCfgOut, a) + } + } + cfg.viper.Set(key, valueCfgOut) + cfg.viper.WriteConfig() +} + +// GetSlice returns the viper config for a specific protocol. +func (cfg *Config) GetSlice(protocol string) []string { + return cfg.viper.GetStringSlice(protocol) +} diff --git a/configByeFuncs.go b/configByeFuncs.go deleted file mode 100644 index 4a1c4c5..0000000 --- a/configByeFuncs.go +++ /dev/null @@ -1,43 +0,0 @@ -package shoset - -import ( - // "fmt" - - "github.com/ditrit/shoset/msg" -) - -// GetConfigBye : -func GetConfigBye(c *ShosetConn) (msg.Message, error) { - var cfg msg.ConfigProtocol - err := c.ReadMessage(&cfg) - return cfg, err -} - -// HandleConfigBye : -func HandleConfigBye(c *ShosetConn, message msg.Message) error { - cfg := message.(msg.ConfigProtocol) // compute config from message - ch := c.GetCh() - dir := c.GetDir() - remoteAddress := cfg.GetAddress() - - switch cfg.GetCommandName() { - case "bye": - if dir == "in" { - cfgNewDelete := msg.NewCfg(remoteAddress, ch.GetLogicalName(), ch.GetShosetType(), "delete") - ch.ConnsByName.IterateAll( - func(address string, bro *ShosetConn) { - if address != remoteAddress { - bro.SendMessage(cfgNewDelete) - } - }, - ) - c.SetIsValid(false) - } - - case "delete": - ch.LnamesByProtocol.Set("bye", cfg.GetLogicalName()) - ch.LnamesByType.Set(cfg.GetShosetType(), cfg.GetLogicalName()) - ch.deleteConn(cfg.GetAddress(), cfg.GetLogicalName()) - } - return nil -} diff --git a/configFuncs.go b/configFuncs.go deleted file mode 100644 index c1d8414..0000000 --- a/configFuncs.go +++ /dev/null @@ -1,63 +0,0 @@ -package shoset - -import ( - "fmt" - "time" - - "github.com/ditrit/shoset/msg" -) - -//TODO MOVE TO GANDALF -// GetConfig : -func GetConfig(c *ShosetConn) (msg.Message, error) { - var conf msg.Config - err := c.ReadMessage(&conf) - return conf, err -} - -// HandleConfig :config -func HandleConfig(c *ShosetConn, message msg.Message) error { - conf := message.(msg.Config) - c.GetCh().Queue["cmd"].Push(conf, c.GetRemoteShosetType(), c.GetLocalAddress()) - return nil -} - -// SendConfig : -func SendConfig(c *Shoset, cmd msg.Message) { - fmt.Print("Sending Config.\n") - c.ConnsByName.IterateAll( - func(key string, conn *ShosetConn) { - conn.SendMessage(cmd) - }, - ) -} - -// WaitConfig : -func WaitConfig(c *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { - commandName, ok := args["name"] - if !ok { - return nil - } - term := make(chan *msg.Message, 1) - cont := true - go func() { - for cont { - message := replies.Get().GetMessage() - if message != nil { - config := message.(msg.Config) - if config.GetCommand() == commandName { - term <- &message - } - } else { - time.Sleep(time.Duration(10) * time.Millisecond) - } - } - }() - select { - case res := <-term: - cont = false - return res - case <-time.After(time.Duration(timeout) * time.Second): - return nil - } -} diff --git a/configJoinFuncs.go b/configJoinFuncs.go deleted file mode 100644 index 7bf7311..0000000 --- a/configJoinFuncs.go +++ /dev/null @@ -1,90 +0,0 @@ -package shoset - -import ( - "errors" - // "fmt" - - "github.com/ditrit/shoset/msg" -) - -// GetConfigJoin : -func GetConfigJoin(c *ShosetConn) (msg.Message, error) { - var cfg msg.ConfigProtocol - err := c.ReadMessage(&cfg) - return cfg, err -} - -// HandleConfigJoin : -func HandleConfigJoin(c *ShosetConn, message msg.Message) error { - cfg := message.(msg.ConfigProtocol) // compute config from message - ch := c.GetCh() - dir := c.GetDir() - remoteAddress := cfg.GetAddress() - - switch cfg.GetCommandName() { - case "join": - if dir == "in" { // a socket wants to join this one - if connsJoin := c.ch.ConnsByName.Get(c.ch.GetLogicalName()); connsJoin != nil { //already joined - if connsJoin.Get(remoteAddress) != nil { - return nil - } - } - - if ch.GetLogicalName() == cfg.GetLogicalName() && ch.GetShosetType() == cfg.GetShosetType() { - c.SetRemoteAddress(remoteAddress) - c.SetRemoteLogicalName(cfg.GetLogicalName()) - c.SetRemoteShosetType(cfg.GetShosetType()) - ch.ConnsByName.Set(ch.GetLogicalName(), remoteAddress, "join", ch.GetShosetType(), c) // set conn in this socket - // ch.LnamesByProtocol.Set("join", c.GetRemoteLogicalName()) - // ch.LnamesByType.Set(c.ch.GetShosetType(), c.GetRemoteLogicalName()) - - configOk := msg.NewCfg(remoteAddress, ch.GetLogicalName(), ch.GetShosetType(), "aknowledge_join") - c.SendMessage(configOk) - } else { - c.SetIsValid(false) - - configNotOk := msg.NewCfg(remoteAddress, ch.GetLogicalName(), ch.GetShosetType(), "unaknowledge_join") - c.SendMessage(configNotOk) - return errors.New("error : Invalid connection for join - not the same type/name") - } - } - - cfgNewMember := msg.NewCfg(remoteAddress, ch.GetLogicalName(), ch.GetShosetType(), "member") - ch.ConnsByName.Get(ch.GetLogicalName()).Iterate( - func(address string, bro *ShosetConn) { - if address != remoteAddress { - bro.SendMessage(cfgNewMember) //tell to the other members that there is a new member to join - } - }, - ) - - case "aknowledge_join": - c.SetRemoteLogicalName(cfg.GetLogicalName()) - c.SetRemoteShosetType(cfg.GetShosetType()) - ch.ConnsByName.Set(ch.GetLogicalName(), c.GetRemoteAddress(), "join", ch.GetShosetType(), c) // set conns in the other socket - // c.ch.LnamesByProtocol.Set("join", c.GetRemoteLogicalName()) - // c.ch.LnamesByType.Set(c.ch.GetShosetType(), c.GetRemoteLogicalName()) - - case "unaknowledge_join": - c.SetIsValid(false) - return errors.New("error : connection not ok") - - case "member": - if connsJoin := c.ch.ConnsByName.Get(c.ch.GetLogicalName()); connsJoin != nil { //already joined - if connsJoin.Get(remoteAddress) == nil { - ch.Protocol(remoteAddress, "join") - - cfgNewMember := msg.NewCfg(remoteAddress, ch.GetLogicalName(), ch.GetShosetType(), "member") - ch.ConnsByName.Get(ch.GetLogicalName()).Iterate( - func(address string, bro *ShosetConn) { - if address != remoteAddress { - bro.SendMessage(cfgNewMember) //tell to the other members that there is a new member to join - } - }, - ) - } - } - - } - return nil -} diff --git a/configLinkFuncs.go b/configLinkFuncs.go deleted file mode 100644 index dfb8bad..0000000 --- a/configLinkFuncs.go +++ /dev/null @@ -1,102 +0,0 @@ -package shoset - -import ( - // "fmt" - - "github.com/ditrit/shoset/msg" -) - -// GetConfigLink : -func GetConfigLink(c *ShosetConn) (msg.Message, error) { - var cfg msg.ConfigProtocol - err := c.ReadMessage(&cfg) - return cfg, err -} - -// HandleConfigLink : -func HandleConfigLink(c *ShosetConn, message msg.Message) error { - cfg := message.(msg.ConfigProtocol) - remoteAddress := cfg.GetAddress() - dir := c.GetDir() - - switch cfg.GetCommandName() { - case "link": - if dir == "in" { // a socket wants to link to this one - if connsLink := c.ch.ConnsByName.Get(c.ch.GetLogicalName()); connsLink != nil { //already linked - if connsLink.Get(remoteAddress) != nil { - return nil - } - } - - c.SetRemoteAddress(remoteAddress) - c.SetRemoteLogicalName(cfg.GetLogicalName()) // avoid tcp port name - c.SetRemoteShosetType(cfg.GetShosetType()) - c.ch.ConnsByName.Set(cfg.GetLogicalName(), remoteAddress, "link", cfg.GetShosetType(), c) // set conn in this socket - // c.ch.LnamesByProtocol.Set("link", c.GetRemoteLogicalName()) - // c.ch.LnamesByType.Set(c.ch.GetShosetType(), c.GetRemoteLogicalName()) - - localBrothers := c.ch.ConnsByName.Get(c.ch.GetLogicalName()) - localBrothersArray := []string{} - if localBrothers != nil { - localBrothersArray = localBrothers.Keys("all") - } - - remoteBrothers := c.ch.ConnsByName.Get(cfg.GetLogicalName()) - remoteBrothersArray := []string{} - if remoteBrothers != nil { - remoteBrothersArray = remoteBrothers.Keys("all") - } - - brothers := msg.NewCfgBrothers(localBrothersArray, remoteBrothersArray, c.ch.GetLogicalName(), "brothers", c.ch.GetShosetType()) - remoteBrothers.Iterate( - func(address string, remoteBro *ShosetConn) { - remoteBro.SendMessage(brothers) //send config to others - }, - ) - } - - case "brothers": - if dir == "out" { // this socket wants to link to another - c.SetRemoteLogicalName(cfg.GetLogicalName()) - c.SetRemoteShosetType(cfg.GetShosetType()) - c.ch.ConnsByName.Set(cfg.GetLogicalName(), c.GetRemoteAddress(), "link", cfg.GetShosetType(), c) // set conns in the other socket - // c.ch.LnamesByProtocol.Set("link", c.GetRemoteLogicalName()) - // c.ch.LnamesByType.Set(c.ch.GetShosetType(), c.GetRemoteLogicalName()) - - localBrothers := cfg.GetYourBrothers() - remoteBrothers := cfg.GetMyBrothers() - - for _, bro := range localBrothers { - if bro != c.ch.GetBindAddress() { - conn, err := NewShosetConn(c.ch, bro, "me") // create empty socket so that the two aga/Ca know each other - if err == nil { - conn.SetRemoteLogicalName(c.ch.GetLogicalName()) - conn.SetRemoteShosetType(c.ch.GetShosetType()) - c.ch.ConnsByName.Set(c.ch.GetLogicalName(), bro, "link", conn.GetRemoteShosetType(), conn) // musn't be linked ! - } - - newLocalBrothers := c.ch.ConnsByName.Get(c.ch.GetLogicalName()).Keys("me") - for _, lName := range c.ch.ConnsByName.Keys() { - lNameConns := c.ch.ConnsByName.Get(lName) - addresses := lNameConns.Keys("in") - brothers := msg.NewCfgBrothers(newLocalBrothers, addresses, c.ch.GetLogicalName(), "brothers", c.ch.GetShosetType()) - lNameConns.Iterate( - func(key string, val *ShosetConn) { - val.SendMessage(brothers) - }) - } - } - } - - for _, remoteBro := range remoteBrothers { // link to the brothers of the socket it's linked with - remoteBrothers := c.ch.ConnsByName.Get(cfg.GetLogicalName()) - if remoteBrothers != nil { - if remoteBrothers.Get(remoteBro) == nil { - c.ch.Protocol(remoteBro, "link") - } - } - } - } - } - return nil -} diff --git a/config_test.go b/config_test.go new file mode 100644 index 0000000..84ccc53 --- /dev/null +++ b/config_test.go @@ -0,0 +1,50 @@ +package shoset + +import "testing" + +var cfg *Config + +func TestNewConfig(t *testing.T) { + cfg = NewConfig("test") + if cfg == nil { + t.Errorf("TestNewConfig didn't work") + } + cfg.SetFileName("test") +} + +func TestInitFolders(t *testing.T) { + TestNewConfig(t) + _, err := cfg.InitFolders(cfg.GetFileName()) + if err != nil { + t.Errorf("TestInitFolders didn't work") + } +} + +func TestSet(t *testing.T) { + TestInitFolders(t) + cfg.AppendToKey("protocol_test", []string{"address_test"}) +} + +func TestWriteConfig(t *testing.T) { + TestSet(t) + err := cfg.WriteConfig(cfg.GetFileName()) + if err != nil { + t.Errorf("TestWriteConfig didn't work" + err.Error()) + } +} + +func TestReadConfig(t *testing.T) { + TestWriteConfig(t) + err := cfg.ReadConfig(cfg.GetFileName()) + if err != nil { + t.Errorf("TestReadConfig didn't work " + err.Error()) + } +} + +func TestGetSlice(t *testing.T) { + TestWriteConfig(t) + addresses := cfg.GetSlice("protocol_test") + if addresses[0] != "address_test" { + t.Errorf("TestGetSlice didn't work ") + } +} diff --git a/const.go b/const.go new file mode 100644 index 0000000..0f58f17 --- /dev/null +++ b/const.go @@ -0,0 +1,115 @@ +package shoset + +import ( + "path/filepath" +) + +// Supported message type +var MESSAGE_TYPES = []string{"cfgjoin", "cfglink", "cfgbye", "pkievt_TLSdoubleWay", "routingEvent", "evt", "cmd", "simpleMessage", "forwardAck", "file"} //added "routingEvent", "evt", "cmd", "simpleMessage", "forwardAck" + +// Has a Send function in the handler +var SENDABLE_TYPES = []string{"routingEvent", "evt", "cmd", "simpleMessage", "forwardAck", "file"} // "config" + +// Has a Wait function in the handler +var RECEIVABLE_TYPES = []string{"evt", "cmd", "simpleMessage", "forwardAck", "file"} // "config" + +var FORWARDABLE_TYPES = []string{"simpleMessage"} + +// empty string +const ( + VOID string = "" +) + +// protocol +const ( + CONNECTION_TYPE string = "tcp" + + // join + PROTOCOL_JOIN string = "join" + ACKNOWLEDGE_JOIN string = "acknowledge_join" + MEMBER string = "member" + + // link + PROTOCOL_LINK string = "link" + ACKNOWLEDGE_LINK string = "acknowledge_link" + BROTHERS string = "brothers" + + // bye + PROTOCOL_EXIT string = "bye" + DELETE string = "delete" +) + +// direction +const ( + OUT string = "out" + IN string = "in" + ME string = "me" // create conn without real direction - see it as a fake connection between 2 shosets from the same type in order to get them to know each other. +) + +// IP +const ( + DEFAULT_IP string = "0.0.0.0:" // analyze all network traffic - "no specific address". + LOCALHOST string = "127.0.0.1" // loopback. +) + +// key +const ( + ALL string = "all" // doesn't restrict key. + RELATIVE string = "me" // restrict keys to relatives from fake connection between 2 shosets from the same type. +) + +// encode type +const ( + CERTIFICATE string = "CERTIFICATE" + RSA_PRIVATE_KEY string = "RSA PRIVATE KEY" +) + +// path : it depends of the os +var ( + PATH_CA_CERT string = filepath.Join("cert", "CAcertificate.crt") + PATH_CERT string = filepath.Join("cert", "cert.crt") + PATH_CA_PRIVATE_KEY string = filepath.Join("cert", "privateCAKey.key") + PATH_PRIVATE_KEY string = filepath.Join("cert", "privateKey.key") + PATH_CONFIG_DIRECTORY string = filepath.Join("config") + PATH_CERT_DIRECTORY string = filepath.Join("cert") + PATH_COPY_FILES string = filepath.Join(".copy") +) + +// TLS +const ( + // TLS double way communication - established protocols. + TLS_DOUBLE_WAY_TEST_WRITE string = "testTLSdoubleWayWrite" + TLS_DOUBLE_WAY_PKI_EVT string = "pkievt_TLSdoubleWay" + + // TLS single way communication - certificate request. + TLS_SINGLE_WAY_PKI_EVT string = "pkievt_TLSsingleWay" + TLS_SINGLE_WAY_RETURN_PKI_EVT string = "return_pkievt_TLSsingleWay" +) + +const ( + // CERT_FILE_ENVIRONMENT is the environment variable which identifies where to locate + // the SSL certificate file. If set this overrides the system default. + CERT_FILE_ENVIRONMENT string = "SSL_CERT_FILE" +) + +// viper +const ( + CONFIG_TYPE string = "yaml" + CONFIG_FILE string = "config.yaml" +) + +// logger +const ( + INFO string = "info" + TRACE string = "trace" + DEBUG string = "debug" + WARN string = "warn" + ERROR string = "error" +) + +// Forward message +const ( + MASTER_SEND_TIMEOUT int = 30 //s + TIMEOUT_ACK int = 5 //s + MAX_FORWARD_TRY int = 3 +) diff --git a/doc/OLD_README.md b/doc/OLD_README.md new file mode 100644 index 0000000..f5d5819 --- /dev/null +++ b/doc/OLD_README.md @@ -0,0 +1,51 @@ +# shoset + +A smart multi-ends socket library. + +## Documentation + +See in `guide.md`.
+Find a usage example [in the gandalf project](https://github.com/ditrit/gandalf-core/blob/master/aggregator/aggregator.go). + +## Design principles + +For now, over to the [team's taiga](https://taiga.orness.com/project/xavier-namt/wiki/shoset). + +## Running tests + +You can run multiple tests in Shoset. There are three types of tests; unit test, script test or functional tests. + +### Unit tests +To run unit tests, you need to go in your editor (I did it on VSCode) in the file with a name corresponding to `*_test.go`, then you can simply click on `run test` or `debug test` button. There is also a dedicated tab in the navigation bar with a flask as icon to run each test one after the other. + +### Script tests (Not working) +To run script tests, you can find two scripts in the folder `shoset/script/` which are `cert_checker.sh` and `shoset_checker.sh`. These tests can be run in a Linux terminal as follows : + +```txt +./script/cert_checker +./script/shoset_checker number_of_files_expected +``` + +#### cert_checker.sh +`cert_checker.sh` is a script done to check the validity of certificates generated for each Shoset after those have run a functional test. It will run a server in background and then run a client who will connect to this server by showing its certificates. If the certificates are valid, then you must see in the file `~/.shoset/cert_checker.txt` the word `ACCEPTED` for each Shoset. + +#### shoset_checker.sh +`shoset_checker.sh` is a script done to launch multiple functional tests one after the other to check if there are no errors occurring when multiples test are run. You need to add the argument `number_of_files_expected` which is an integer representing the number of files you want to be obtained after each test. For example, if you have a network composed of 4 Shoset (1 PKI and 3 classic Shoset), then you will obtain 17 files (4 for the PKI, 3 for each classic Shoset and 1 config file for each Shoset - depending on your network complexity, there is not always a config file). To precisely know the number of file you need, you can simply run a functional test one time and see how many files you have by running this command : +```txt +tree ~/.shoset/ +``` +After the script is run, you can watch if errors occurred in the file `shoset/log_error.txt`, if this file is empty, then it means that you are good to go ! + +### Functional tests + +You can run multiple functional tests in the file `shoset/test/test.go` with this command : +```shell +go run test/test.go arg +``` +The arg argument corresponds to the test you want to run. Take a look at the test file and adapt the test if you want. Don't forget to remove existing certificates and config files in `~/.shoset/folder`. + +You can set an alias to run tests easily : `alias shoset='rm -rf ~/.shoset && timeout 30s go run -race test/test.go 4 > log.txt 2>&1'`. It removes existing files, timeouts the test at 30 seconds (it shouldn't last more than 15 seconds except if the network is large), runs the test with -race argument to detect data races and print the output in a log.txt file that you can find in the `shoset/` folder. + +If you need to kill a Shoset at running time for testing. Then I advise you to launch two terminals. In the first one you will run your program with 2 as arg which will run the simpleCluster() function that simply creates a PKI Shoset. In the other terminal, run your program with 4 as arg which will run the testJoin4() function that creates multiple classic Shosets. Then you can `CTRL+C` in one of the two terminal to kill the Shoset(s). Of course, you need to adapt the call to the function corresponding to your arg in the main() function of the test file. + +If you want to create your own test, don't forget that the network needs that the first Shoset is initialized as a PKI with InitPki() function and the other with the Protocol() protocol - most of the function are deprecated because they do not use InitPKI() nor Protocol(), please don't use them. \ No newline at end of file diff --git a/doc/fileSynchronisation.md b/doc/fileSynchronisation.md new file mode 100644 index 0000000..d121980 --- /dev/null +++ b/doc/fileSynchronisation.md @@ -0,0 +1,118 @@ +# How does the file Transfer works ? + +### Quick Introduction +The aim of this feature is to distribute as quick as possible a file among multiple nodes. +Moreover, all nodes having the same logical name should have the same files in their library. A library is just a repository on the node 's machine where all the distributed files are stored. +In addition, the system should be self-managing. It adapts to the configuration in which it is used, thus ensuring the best efficiency in most situations.For instance, this sharing must work for 1MB files as well as 10GB files. And it must be efficient, whether there are 2 nodes in the pool or 40 nodes in the pool. +Many directions have been taken during the developpment and this version might not be the optimal one. + +### Useful Vocabulary +- **seeder**: node that has the entire file and distributes it to others +- **leecher**: node that has the file but incompletely: it shares and downloads the file at the same time +- **piece**: a file is broken down into different pieces of equal size. The pieces are between 256Kb and 1Mb. We try to make sure that there are maximum 10 000 pieces. +- **block** : a piece is decomposed in different blocks of 16Kb size. Thus, a piece is composed of 16 to 64 blocks. +- **bitfiled** : list where there is the number of pieces that the node has +- **pool** : set of nodes having the same logical name +- **library** : it designs the set of files that a nod has. +- **hashMap** : dictionnary of all the pieces id and the hash correspondig to this piece + +## Overview of the Principles + +![Example of a Library.svg](./img/schema_library.svg) +This is how a library is organised. When we create a shoset, we can set a folder to be the library. Inside the .copy directory is a replica of the library. We call the file inside *copy files*. Inside the .info are some information about the temp files. This is used to keep a fingerprint of the each file and avoid rescanning the entire library at each time. + +The .copy folder is always synchronised with the other node. When some modifications are made by the node, it is then copied to the .copy folder and synchronised with all the other nodes.When we received a new file for instance in the .copy directory, we copy it to the library. + +When the libray has changed and a file has been modified (or a new file appear), all the nodes in the same Logical Name (LName) must download it. They get the hashMap of the pieces of the file (dictionnary with the piece number and the hash of the piece) and start to download it. Like Bittorrent Algorithm, there is no central node deciding who send which pieces to who, but each node ask to the other the pieces that they miss. In order to be fast, 2 nodes shouldn't download the same node from the seeder all along. +They should also download from each other if possible. To do so, a node know exactly the *bitfield* of the other nodes. A *bitfield* is a dictionnary storing the id of pieces that a node have. So each node know exactly which nodes have which pieces. A node always ask for the rarest piece on the network so that they can give it to the others afterward. + +## How the download works +A file is separated into pieces of length between 256KB and 1MB (depending on the size of the file). Each pieces is separated into blocks of 16KB. +The main difficulty with file transfer is to know how much data we can send / ask to another node. We don't want to overflow the network nor to underuse it. We decided to build a TCP-like algorithm. We begin with asking small packets of 16KB 2 by 2. Then, if there is no timeout and after a certain time, we can upgrade what we can call a *level*. We ask the data 4 by 4, then 8 by 8, ... until we ask 16 by 16 for a 256KB piece or 64 by 64 for a 1MB piece. When we reach this level, we begin to ask pieces by pieces, then 2 pieces by 2 pieces and so on. +When we detect a timeout in a packet we asked or when we detect that the network is congested, we decrease from one level. We also temporary slow our demand to wait for the timeout pieces. After a period of time, we can increase again. +This algorithm is not exactly the same as TCP but is good enough to adapt to any type of networks with different bandwidth. + +A major difficulty is to fix the different variables as the timeout or the time before 2 increase. For now, these variables are arbitrary fixed but it can be changed. +Another difficulty is to detect a congestion on the connection, and to know what to do with it. + +#### What do we do when we find a slow down ? +Imagine we have identified a connection that just became bad after a good rate. What de we do with that ? The first idea is to warn the other node that ther is a slow down and we can reduce our downloading rate by decreasing from one level. But this slow down may be caused by another node which just connected and asked for too much data (or sent too much data). So we are penalizing the wrong node. +The big problem is that we don't know the topology of the network. Therefore, we can't know which node we should blame for this slow down. +The method implemented is to penalize also the nodes that have the most pieces because there are less hurried to download pieces. So we penalize randomly the nodes, those who have a higher rate are more likely to be blamed. +This is not the optimal solution, we should dig deaper this question. + + +#### How we monitor the flow rate : Using the system.calls to access TCP information +TCP protocol already have a congestion avoidance algorithm. On Linux, we can access this data because we can access the system calls (on Windows, it is not accessible). +These are a lot of data. The most interesting is the *RTT* (Round-Trip delay Time). It is the time (in average) between the moment we send the packet and we receive the acknowledgment. If we see that the RTT increased a lot, the connection is then a slow connection. +This data is also stored in *flowrate.csv* in the folder *.info*. It can be usefull to analyse it after a download. + +#### How does the user / programm do changes in the Library ? +The user / programm will have to go through fucntions (add, delete, move, modify) to perform operations. But instead of only doing these operations, it go through a lock system. +Just before doing the operation, the node try to "lock" the library. +The *Lock* function will ask to the other node in the same LName if it can edit the library. If the library is not up to date or an operation is performed on another node, the answer is no : the function then return an error. At the end of the operation, the library is "unlocked". +For now, this solution is not fully tested but works for the *Add* function. + +## Structure of the code +- Inside the shoset repository, we have the directory **file** where all the codes linked to file manipulation is stored. Each file in the library and in the copy of the library has a **file** instance. It is mostly used to read file, write to a file, calculate the hash of the file. +- We also have a specific file in the folder **msg** called *fileTransfer.go* which is used to send file information on the network +- *handleFileTransfer.go* takes care of handling the file messages received +- *syncFiles.go* add functions to the shoset instance +- **file_test** have a few unit tests from **file** + +### Inside the file folder +- *file.go* : it has the File interface ant the FileImpl structure. This is used to do operations on one file (read, write data, calculate hash of the file, have the version, ...). Each file in the library and in the copy of the library has a **file** instance. +- *syncFile.go* : it has the SyncFile interface and the SyncFileImpl structure. It makes the link between the real file (the file in the library) and the copy file (the file in the .copy folder). Each SyncFile has a uuid (unique identifier). This uuid is used to identify the file we are talking about. It is usefull when a file is renamed. SyncFile is also used to keep track of the operations made on the file and to update the files in both the library or the library copy. + It also has the FileState structure used to get some information about a SyncFile +- *operations.go* : it is used to identify an operation made on a file (add, modifiy, remove, move) +- *fileLibrary.go* : this instance is used to manage the library (mostly to add or remove a file) , e.g to track all the files in the library +- *externalCommands.go* : This package provide accessible commands to the user. The commands are : move, delete, add, modify. Use these commands to modify a file in the library to do it safely. It asks to the other node for their permission to modify the library. +- *fileSeeder.go* : this small structure is used when a node ask us a chunk of a file. We load the data and send it back +- *fileLeecher.go* : it kinds of inherits from fileSeeder. This huge structure is in charge of downloading a single file from other nodes (and also uploading the parts we received thanks to the fileSeeder). This is inspired by the bittorrent protocol. +- *connInfo.go* : it stands for Connection Information. It keep tracks some information about a connection with another node when we are downloading a file. In particular how much data we can ask him at the same time. This amount is separated into levels and specific strategy is runned to keep the optimal download rate. There is a connInfo instance per fileLeecher per conn. +- *piece.go* : A file is separated into pieces of lenght between 256ko and 1Mo. The piece instance is used to store some information about a piece (including the hash of the piece). As a piece length may be to big to be asked / sent on the network, a piece can be divide into small blocks of 16ko. +- *logRateTransfer.go* : It stores in a csv file the flowrate of each connection. Initialy, it was used to store the upload and download rate. With the new variable used to monitor the flowrate, it is only storing this variable : the RTT of each request. +- *fileMessageQueue.go* : It implements a queue with a channel and lock functionnality. It is used if FileTransfer to handle messages to send and to receive. +- *fileTransfer.go* : The most important file. It handles all the functionnality and messages of the file synchronisation process. It also has a Rate structure to monitor theflow rate and prevent congestion. + +## Goroutine +We use multiple goroutines to have more efficent file transfer. Before the file transfer, they were a goroutine per TCP connection. Now, there is also a goroutine to handle send messages and received messages per connection. This is why Locks are used to handle access to ressources. + +#### Locks +We use *sync.mutex* to lock and unlock structure / objects. Some objects ahve only Lock and Unlock, some other have also RLock and RUnlock (for only read : do not block other reader) + + +# Tools + +### Ifacemaker +This tool can be used to generate an interface from a structure. +link to install : https://github.com/vburenin/ifacemaker +Example : +```bash +ifacemaker -f file/file.go -s FileImpl -i File -p file +# generates the interface File for the structure FileImpl and the package file +``` + +### Mockgen +This tool can be used to simulate the behaviour of an object. When we have an interface, it creates a structure so that we know which functions are called. To generate it : +```bash +mockgen -destination="test/mocks/file/fileLibrary.go" -source="file/fileLibrary.go" +``` +It can then be used in tests by adding generally Mock before the interface name. + + +# Tests +Some Unit tests have been made in the file_test folder. + +### Simulate traffic limitation +- To do test with different bandwidth limitation, you can use the tool *TrafficTroll* +There is the file *test/traffic.yaml* which is the configuration. +link to install : https://github.com/cryzed/TrafficToll +Example : +```bash +sudo tt lo ./file_test/traffic.yaml --delay 0.5 +# tt is in ~/.local/bin +``` +- The tool *trickle* doesn't work with golang. +- You can also use *iproute2* and *tc* but it is more complex to handle. + diff --git a/doc/guide.md b/doc/guide.md new file mode 100644 index 0000000..65b97cc --- /dev/null +++ b/doc/guide.md @@ -0,0 +1,291 @@ +This file is what I would have liked to have on hand when I started on the project to understand how it works. + +This is **not an exhaustive documentation** of the project (There is no such thing right now, but you are welcomed to create one). + +Please keep this file concise and up to date with the latest changes when leaving the project, think about the next guy. + +This project contains code that is cryptic, suboptimal or that doesn’t behave as expected (and sometime all of the above), don’t be afraid of improving it. + +If you struggle to understand something, please come back here to explain it once you’ve figured it out. + +# Shoset : + +Shoset is a smart Socket library, **resilient**, **distributed** and with **high availability**. + +**Resilient :** automatic reconnection and no startup order (As long as every shoset is certified.) . + +**Distributed :** with the brother system, any **shoset** can fail with no disruption to the network if it is not alone in its logical name. + +**High availability :** Built in redundancy. + +Send and receive data in the form of messages between **shosets**. + +## Vocabulary : + +- **Shoset :** A smart socket from this library. +- **Logical name** (**Lname**) (string) : A name describing the role of the **shoset** in the network, 2 **shosets** with the same **Lname** have the same role and can be used interchangeably. +**Shosets** with the same Lname are **brothers.** +- **Brother :** Remote **Shoset** of the same **Lname**, state is synced between them. +- **Message :** How **shosets** exchange data. +- Types of connection between two **Shosets** : + - **Join :** Everybody has a direct link to everybody in a **join**. + - **Link :** Connection are only established point to point in a **link**. +- **PKI system :** In order to join the network by establishing a connection to another **shoset**, it must be certified by the **CA** (certificate authority) **shoset** of the network. +- **Certification :** Obtaining a certificate from the **CA** **shoset**. +- **Network :** every **shosets** connected together. + +## Module Architecture : + +### Diagrams : + + In svg form in the `./uml` folder. (Open it with a web browser for highlightable and copy-pasteable names.) + +**Class diagram :** + +- [https://github.com/jfeliu007/goplantuml](https://github.com/jfeliu007/goplantuml) + +**Call diagram :** + +- Call diagram are also available but are not very readable. +- [https://github.com/ofabry/go-callvis#interactive-viewer](https://github.com/ofabry/go-callvis) + +Update diagrams with script : `./uml/generate_diagrams_svg.sh` (Installation instructions for necessary software in the comments of the script.) + +(Also see tools : cpu profiler, memory profiler, tracer, …) //Lien vers l’autre fichier avec les explications + +**Shoset :** + +- **ShosetConn :** connection to another remote **shoset**. +- **Certification :** + - To get certified, you need to have a direct or indirect connection to the **CA** **shoset.** + - To establish a connection with a **shoset**, it must be already certified. + - Certificates will be reused for the next launch. +- **Route :** Information to forward a message to a **Lname** to which the **shoset** may not have a connection. +- Message type ([Creating a custom message type :](guide%2030e418b93d85452d83f0dc7c627318c7.md)) : + - **Table of message types :** (config ≠configProtocol) + + | Message type : | Usage : | Internal : | Send type : | Forwarding : | Queue : | + | -------------------------------- | ------------------------------------------------------------------- | ---------- | ------------------------------------------------ | ------------ | ------- | + | command | Command for Gandalf | No | Send to every connection | No | Yes | + | config | Command for Gandalf | No | Send to every connection | No | Yes | + | configProtocol (link, join, bye) | Used for establishing and breaking connections (join, link, bye, …) | Yes | None (Handled outside the handler) | No | No | + | event | Send payload to the entire network | No | Send to every connection | No | Yes | + | pkiEvent | PKI initialization for establishing secure connexion | Yes | Send to every connection | No | Yes | + | routingEvent | Announce a shoset to the network to establish route to it. | Yes | Send to every connection | No | No | + | simpleMessage | Simplest forwardable message type | No | Use route system to send only to the destination | Yes | Yes | + | ForwardAck | Acknowledge the reception of a forwarded message. | Yes | None (Handled outside the handler) | No | Yes | + - Every messageType has : + - Common basic attributes (**MessageBase**) and some type specific attributes (Payload, UUID, TimeStamp, …). + - An **identifying string** used to access the handler specific to this massage type. + - Dedicated **handler**. (`./handler_*message_type*.go`) + - Definition of the message type : specific attributes, Getter/Setter, **identifying string**, … (**`./msg/*messageType*.go`**) +- **Handler :** Set of functions specific to a message type. + - Explicitly called (Generic version calling the correct function from the handler are available in `shoset.go`): + - `Send()` : Sends a message. + - `Wait()` : Waits for a message to be available and returns it. + - Internal : + - `HandleDoubleWay()` : Is called when a new message is received, takes actions specific to the message type (Add to **queue**, send to others, …) + - `Get()` : Is used to retrieve messages before they are processed. +- **Queue :** + - Where received messages are stored. + - Messages are automatically deleted from the **queue** after a set (on a per messages basis) number of milliseconds `( int message.timeout (millisecond) )` +- **Iterator :** + - Used to access the message **queue** and avoid retrieving the same message twice. + - A message is consumed by a specific **iterator** only for itself (pointer to the same object), and is still available to everybody else. + +- **EventBus :** + - Used for internal communication between goroutines for new available data (message, shoset ready, …) + - A data race can occur (and is harmless) when an event is published and a channel is unsubscribed before the publication to this specific channel is done. +- **ConcurentSlice :** + - Thread safe slice with built-in functions to wait for the next change (`WaitForChange()`) or for the slice to be empty (`WaitForEmpty()`). + - Primarily used to track connection initialization for a **shoset.** +- **MapSyncMap :** + - Mainly used to store information about **shosetConns** and **shosetConns** themselves. + - syncMap to a syncmap : `ConnsByLname map[lName](map[remoteAddress]*ShosetConn)` + - System to save part of the content to the `config.yaml` file. +- **Configuration storage :** + - Every **shoset**’s configuration is stored in a dedicated folder located at : `~/.shoset` (Home folder). + - It should be deleted for every test : `rm -rf ~/.shoset`(automatically deleted if you use the scripts (`./test/run_shoset.sh N`) or the VScode launch tasks for the debugger to launch the test). + - Folder structure : + + ``` + https://tree.nathanfriend.io/?s=(%27options!(%27fancy!true~fullPath!false~trailingSlash!true~rootDot!false)~4(%274%270.shoset0*Lname0**BindAddress-36CA3ificate.crt63.crt2CA5%20%7BOnly%20for%20the%20CA%20shoset%7D25-config6config.yaml66%27)~version!%271%27)*%20%20-0***0%5Cn*26private3cert4source!5Key.key6-*%01654320-* + + .shoset/ + └── Lname/ + └── BindAddress/ + ├── cert/ + │ ├── CAcertificate.crt + │ ├── cert.crt + │ ├── privateCAKey.key (Only for the CA shoset) + │ └── privateKey.key + └── config/ + └── config.yaml (Used to relauch connections automaticaly) + ``` + +- **Connection types :** + - **Single way :** Used only for the certification process. + - **Double way :** Used for normal communication. +- **Certification process :** + - Use a Temporary connection (**single way**) to obtain certificates from **CA** (certificate authority) **shoset** of the network. + - The certificate request is forwarded to the CA shoset (not using the **route** system). + +### Files and folders of interest : + +Everything using the types **Shoset** and **ShosetConn** must be in the **shoset** package and therefore at the root of the project folder. + +This makes for a very untidy and confusing project to browse, but given the limitation of golang on where the code can be located, there is no other easy solution. + +- handler_*message_type*.go +- shoset.go +- shosetConn.go +- msg + - *messageType*.go +- const.go +- test/ + +### Files and folders you should not have to touch : + +- msg + - queue.go + - iterator.go + - reader.go + - writer.go +- pki.go +- concurent_data +- event_bus + +## Routing : + +Routing enables a **shoset** to send a message (of a forwardable type) specifically to another **shoset** to which it has no direct connection (**forwarding** a message). + +A **routing event** only broadcasts the existence of a single **Lname** (and is generated by the **Lname** it is advertising), but it can trigger a **routing event** for other **Lnames** it goes through. + +The first **routing event** going through the **network** will trigger the entire network to reroute (Which will generate many messages). + +Every change to the network triggers a full reroute. + +### Handling of a routing event for a given Lname : + +A **routing event** will be broadcasted through the **network** by every node that deems it worth saving. + +Every routing event also has a unique ID and a timestamp to always choose the most up-to-date **route**. + +| Routing event case : | Action : | +| ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| ≠ID and more recent timestamp | Save route (no mater the length)
Reroute self (new routing event with new ID)
Rebroadcast Routing event to every connection | +| ≠ID and older timestamp | do nothing | +| Better route (fewer steps than the current route) | Save route (no mater the length)
Reroute self (new routing event with new ID)
Rebroadcast Routing event to every connection | +| Route to unknown Lname (No route to this Lname) | Save route (no mater the length)
Reroute self (new routing event with new ID)
Rebroadcast Routing event to every connection | +| Worse route with same ID as the current route | do nothing | + +Only one **route** per **Lname** is saved at any give moment by a **shoset**. + +![Routing Shoset.drawio.svg](./img/legend_routing.svg) + +![Routing Shoset.drawio (1).svg](./img/schema_routing.svg) + +### Forwarding : + +Trying to send a message to a **Lname** with no **route** will trigger a reroute, and wait for a **route** to the destination to be available. (The new ID will trigger the destination to reroute itself). + +When a message is **forwarded** to the next step of the **route**, an **acknowledgment** (`forwardAck`) is expected, if it is not received before the timeout, the **route** is deleted and a reroute is launched. + +## File Synchronisation : + +This feature is still in developpement. +Each node have a Library (folder on the machine) that will be synchronised with the other nodes belonging to the same Lname. +Basic operations (add a file, delete, move, modify) are supported. +To have a better understanding of the feature, you can read the [File Synchronisation](./fileSynchronisation.md) documentation. + + +## Using the project : + +### Running the project : + +Run the script to run the test with : `./test/run_shoset.sh 7` (No need to build it first) (Number to select the test to run.) + +To run test on multiple processes in order to test recovery and reconnection, run the VScode task : `RunMultiProcesses` + +### Building the project : + +```bash +go build -v -o ./bin/shoset_build -race -gcflags=all="-N -l" ./test/test.go +``` + +`-o` : names the binary +`-gcflags=all="-N -l"` : disables some optimization to enable attaching a debugger to the process + +### Code Example : + +To run an example : `./test/run_shoset.sh 7` (Change the example that runs in `main() (./test/test.go)`) + +**Simplest example :** + +```go +func simpleExample() { + cl1 := shoset.NewShoset("A", "TypeOfA") + cl1.InitPKI("localhost:8001") // Is the CA of the network + + cl2 := shoset.NewShoset("B", "TypeOfB") + cl2.Protocol("localhost:8002", "localhost:8001", "link") // we link it to our first socket + + cl1.WaitForProtocols(5) // Wait for cl1 to be ready + cl2.WaitForProtocols(5) + + //Sender : + time.Sleep(1 * time.Second) + event := msg.NewEventClassic("test_topic", "test_event", "test_payload") + cl2.Send(event) + + //Receiver : + event_rc := cl1.Wait("evt", map[string]string{"topic": "test_topic", "event": "test_event"}, 5, nil) + fmt.Println("event received : ", event_rc) + shoset.Log("event received (Payload) : " + event_rc.GetPayload()) +} +``` + +See more examples in `./test/example.go` + +**Topology (`./test/utils_for_test/`) :** + +Defines a set of **Lnames**, assigns an IP address to each and establishes the connections between them. + +A set of functions is also available for easy launch of a topology in a single process or a multiprocesses scenario. + +![Routing Shoset topology.svg](./img/routing_topology.svg) + +### Logger : + +**Looging to file :** `command > log.txt 2>&1` + +**Logger :** + +There are dedicated instances of a logger in every shoset and shosetConn. + +```go +// You can log data to the standard ouput with : +shoset1.Logger.Debug().Str("key", value).Msg("Message to log") +//or +shoset1.Logger.Error().Msg("Message to log.") + +//There are many logger levels : Info(),Warn(), Debug(),Trace(), Error(),Panic() and Fatal(). + +//Set the log level with : +shoset.SetLogLevel(shoset.TRACE) +``` + +## Creating a custom message type : + +Create a new **handler**. (You can use `handler_event.go` or `handler_simple_message.go` as a Template.) + +Create a new message type in a dedicated .go file in `./msg` **(Only public fields will be sent, make every field public in the message struct.)** (You can use `SimpleMessage.go` or `event.go` as a Template.) + +Add the **handler** and a new **queue** to `shoset.go` (in `NewShoset()`). + +Add the identifying string to the lists in `const.go` : + +- of message types (`MESSAGE_TYPES`). +- of forwardable messages if it is forwadable (`FORWARDABLE_TYPES`). +- of `SENDABLE_TYPES` if the `Send()` function is defined in the **handler.** +- of `RECEIVABLE_TYPES` if the `wait()` function is defined in the **handler.** \ No newline at end of file diff --git a/doc/img/cpu_graph.png b/doc/img/cpu_graph.png new file mode 100644 index 0000000..f9bd455 Binary files /dev/null and b/doc/img/cpu_graph.png differ diff --git a/doc/img/flame_graph.png b/doc/img/flame_graph.png new file mode 100644 index 0000000..3084c2f Binary files /dev/null and b/doc/img/flame_graph.png differ diff --git a/doc/img/legend_routing.svg b/doc/img/legend_routing.svg new file mode 100644 index 0000000..609fbf1 --- /dev/null +++ b/doc/img/legend_routing.svg @@ -0,0 +1,4 @@ + + + +
Legend
Legend
Legend
Legend
(A,1)
(A,1)
Lname of the broadcasted node
Lname of th...
Number of steps from the origin
Number of s...
Routing event being broadcasted through the network from one node to another
Routing event being broadca...
[A]={B,2}
[A]={B,2}
Lname of the destination
Lname of th...
Neighbour
(first step of the route)
Neighbour...
Length of the route (in number of steps)
Length of th...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/doc/img/routing_topology.svg b/doc/img/routing_topology.svg new file mode 100644 index 0000000..f0f6e5a --- /dev/null +++ b/doc/img/routing_topology.svg @@ -0,0 +1,4 @@ + + + +
I
I
F
F
D
D
E
E
A (pki)
A (pki)
C
C
B
B
H
H
G
G
B
B
A (pki)
A (pki)
B
B
A (pki)
A (pki)
C
C
Line2
Line2
Line3
Line3
B
B
A (pki)
A (pki)
C
C
StraightLine
StraightLine
D
D
E
E
LinkedCircles
LinkedCircles
E
E
A pki
A pki
D
D
B
B
C
C
Circle
Circle
Text is not SVG - cannot display
\ No newline at end of file diff --git a/doc/img/schema_library.svg b/doc/img/schema_library.svg new file mode 100644 index 0000000..4f455c2 --- /dev/null +++ b/doc/img/schema_library.svg @@ -0,0 +1,16 @@ + + + + + + + my_libraryfolder_1text_1.txttext_2.txttext_3.txt.copyfolder_1text_1.txttext_2.txttext_3.txt.infouuid_1.txtuuid_2.txtuuid_3.txt \ No newline at end of file diff --git a/doc/img/schema_routing.svg b/doc/img/schema_routing.svg new file mode 100644 index 0000000..677fd9f --- /dev/null +++ b/doc/img/schema_routing.svg @@ -0,0 +1,4 @@ + + + +
Network topology
Network topology
(A,1)
(A,1)
Data Store in the routing table of the node :
Data Store in the routing...
A
A
B
B
C
C
D
D
E
E
(A,2)
(A,2)
(B,1)
(B,1)
(B,1)
(B,1)
(C,2)
(C,2)
(D,2)
(D,2)
(E,3)
(E,3)
(C,1)
(C,1)
(C,1)
(C,1)
(D,2)
(D,2)
(E,3)
(E,3)
(A,4)
(A,4)
(B,2)
(B,2)
(A,3)
(A,3)
(C,2)
(C,2)
(D,2)
(D,2)
(D,1)
(D,1)
(D,1)
(D,1)
(E,2)
(E,2)
(E,2)
(E,2)
(B,2)
(B,2)
(E,1)
(E,1)
(A,2)
(A,2)
(B,1)
(B,1)
(C,2)
(C,2)
A
A
B
B
C
C
D
D
E
E
[A]={A,0}
[A]={A,0}
[B]={B,1}
[B]={B,1}
[C]={B,2}
[C]={B,2}
[D]={B,2}
[D]={B,2}
[E]={B,3}
[E]={B,3}
[A]={A,1}
[A]={A,1}
[B]={B,0}
[B]={B,0}
[C]={C,1}
[C]={C,1}
[D]={D,1}
[D]={D,1}
[E]={D,2}
[E]={D,2}
[A]={B,2}
[A]={B,2}
[B]={B,1}
[B]={B,1}
[C]={C,0}
[C]={C,0}
[D]={D,1}
[D]={D,1}
[E]={D,2}
[E]={D,2}
[A]={B,2}
[A]={B,2}
[B]={B,1}
[B]={B,1}
[C]={C,1}
[C]={C,1}
[D]={D,0}
[D]={D,0}
[E]={E,1}
[E]={E,1}
[A]={D,4}
[A]={D,4}
[B]={D,2}
[B]={D,2}
[C]={D,2}
[C]={D,2}
[D]={D,1}
[D]={D,1}
[E]={E,0}
[E]={E,0}
E
E
A pki
A pki
D
D
C
C
B
B
Node triggering the routing event :
Node triggering the r...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/doc/img/tracer.png b/doc/img/tracer.png new file mode 100644 index 0000000..b0bbe73 Binary files /dev/null and b/doc/img/tracer.png differ diff --git a/doc/todo_list.md b/doc/todo_list.md new file mode 100644 index 0000000..59fdffe --- /dev/null +++ b/doc/todo_list.md @@ -0,0 +1,23 @@ +# Ideas for Improvements and changes : + +- Add a description to the GitHub repository. +- Integration of the routing system into other message types. + - Use route system to forward PKI requests + - command +- Redo the polling based wait in some handlers with the event based system from event, simple mesage and forwardAck. +- Finish adaptation to arbitrary network topology. +- Cleaning up Gandalf specific things that are not used anymore. +- Rerouting the entire network every time is not optimal. +- Comprehensive status system for shosets and shosetConns using the event bus. +- Optimize mutex in Shoset and ShosetConn. +- Test with many shosets in a single Lname. +- Separate the project in smaller packages : + - Put mapsyncmap in a dedicated package. + - Put config in a dedicated package. +- Launch at the same time multiple shosets. +- Fix script `cert_checker.sh`. +- Function to use Protocol without relaunching saved connections. +- Need an IDE agnostic way to launch many scripts in many dedicated terminals at once. (Or Doker containers). +- `README.md` : + - Fix the links. + - Update the content. \ No newline at end of file diff --git a/doc/tools.md b/doc/tools.md new file mode 100644 index 0000000..592a699 --- /dev/null +++ b/doc/tools.md @@ -0,0 +1,145 @@ +# Tools for debugging and testing : + +## VScode task : + +**Launching many instances in parallel :** + +Add to this task to `./.vscode/tasks.json`. + +```json +{ + "label": "RunMultiProcesses_run", + "runOptions": {}, + "dependsOrder": "parallel", //Launches every subtask in parrallel. + "dependsOn": [ //There is no way of controlling the order of launch of the parrallel tasks (Terminals are going to be in a random but always the same order) + "RunMultiProcesses_B", + "RunMultiProcesses_A" + ], + "problemMatcher": [ + "$go" + ] +}, +{ + "label": "RunMultiProcesses_A", + "type": "shell", + "command": "RunProcessA", + "presentation": { // Creates a new terminal in the group and run the task in it. + "group": "terminalGroup" + } +}, +{ + "label": "RunMultiProcesses_B", + "type": "shell", + "command": "RunProcessB", + "presentation": { + "group": "terminalGroup" + } +}, +``` + +**Attaching a debugger to an instance :** + +Build command for a binary compatible with attaching a debugger : `go build -v -o ./bin/ -race -gcflags=all="-N -l" ./test/*.go` (`-gcflags=all="-N -l"`disables some optimizations to allow debugging.) + +```json +// To use on the launched process +{ + "name": "Attach to Process", + "type": "go", + "request": "attach", + "mode": "local", + "processId": "${command:pickGoProcess}" +}, +``` + +## CPU Profiler : + +Tells you which functions are using a lot of processing power (in CPU usage time). + +Explore the profile in a web browser with the command : `go tool pprof -http=":" ./shoset_build "./profiler/cpu.prof”` + +Interactive command line : `go tool pprof ./shoset_build "./profiler/cpu.prof”` then top10 (or topN) for the 10 function used the most CPU time. + +This code added to the start of a program generates a `cpu.prof` file in `./profiler/`. + +```go +var cpuprofile = flag.String("cpuprofile", "./profiler/cpu.prof", "write cpu profile to `file`") + +// Clear the content of the profiler folder +os.RemoveAll("./profiler/") +os.MkdirAll("./profiler/", 0777) + +flag.Parse() +if *cpuprofile != "" { + f, err := os.Create(*cpuprofile) + if err != nil { + log.Fatal("could not create CPU profile: ", err) + } + defer f.Close() // error handling omitted for example + if err := pprof.StartCPUProfile(f); err != nil { + log.Fatal("could not start CPU profile: ", err) + } + defer pprof.StopCPUProfile() +} +``` + +### Simple graph : + +How to read the graph : [https://github.com/google/pprof/blob/main/doc/README.md#interpreting-the-callgraph](https://github.com/google/pprof/blob/main/doc/README.md#interpreting-the-callgraph) + +It shows the relation between the function that used the most CPU time and how much they used. + +![Untitled](./img/cpu_graph.png) + +### Flame graph : + +Another way of visualizing the same data : + +![Untitled](./img/flame_graph.png) + +### Source : + +A breakdown of the exact line of code using the most time. (Going down to the exact CPU instruction). + +## Memory Profiler : + +It can produce the same graphs and data as the cpu profiler but for RAM usage : graph, flame graph, source code annotation, … + +Open the profile with the same command as the CPU profiler. + +```go +var memprofile = flag.String("memprofile", "./profiler/mem.prof", "write memory profile to `file`") + +//Memory profiler must run at the end (after your program). + +if *memprofile != "" { + f, err := os.Create(*memprofile) + if err != nil { + log.Fatal("could not create memory profile: ", err) + } + defer f.Close() // error handling omitted for example + runtime.GC() // get up-to-date statistics + if err := pprof.WriteHeapProfile(f); err != nil { + log.Fatal("could not write memory profile: ", err) + } + } +``` + +## Tracer : + +Visual representation of the code flow of the entire project. + +Add this code to the start of the program to enable tracing. + +```go +f, _ := os.Create("./profiler/trace.out") +defer f.Close() +trace.Start(f) +defer trace.Stop() +``` + +Only shows goroutine and functions called, not the detail of the calls. + +Makes obvious moment when goroutines are paused for optimization. + +![Untitled](./img/tracer.png) \ No newline at end of file diff --git a/eventFuncs.go b/eventFuncs.go deleted file mode 100644 index 46401f1..0000000 --- a/eventFuncs.go +++ /dev/null @@ -1,71 +0,0 @@ -package shoset - -import ( - "fmt" - "time" - - "github.com/ditrit/shoset/msg" -) - -// GetEvent : -func GetEvent(c *ShosetConn) (msg.Message, error) { - var evt msg.Event - err := c.ReadMessage(&evt) - return evt, err -} - -// HandleEvent : -func HandleEvent(c *ShosetConn, message msg.Message) error { - evt := message.(msg.Event) - fmt.Println("Shoset") - c.GetCh().Queue["evt"].Push(evt, c.GetRemoteShosetType(), c.GetLocalAddress()) - return nil -} - -// SendEventConn : -func SendEventConn(c *ShosetConn, evt interface{}) { - fmt.Print("Sending config.\n") - c.WriteString("evt") - c.WriteMessage(evt) -} - -// SendEvent : -func SendEvent(c *Shoset, evt msg.Message) { - fmt.Print("Sending event.\n") - c.ConnsByName.IterateAll( - func(key string, conn *ShosetConn) { - conn.SendMessage(evt) - }, - ) -} - -// WaitEvent : -func WaitEvent(c *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { - topicName, ok := args["topic"] - if !ok { - return nil - } - eventName := args["event"] - term := make(chan *msg.Message, 1) - cont := true - go func() { - for cont { - message := replies.Get().GetMessage() - if message != nil { - event := message.(msg.Event) - if event.GetTopic() == topicName && (eventName == "" || event.GetEvent() == eventName) { - term <- &message - } - } else { - time.Sleep(time.Duration(10) * time.Millisecond) - } - } - }() - select { - case res := <-term: - cont = false - return res - case <-time.After(time.Duration(timeout) * time.Second): - return nil - } -} diff --git a/event_bus/event_bus.go b/event_bus/event_bus.go new file mode 100644 index 0000000..a04898e --- /dev/null +++ b/event_bus/event_bus.go @@ -0,0 +1,88 @@ +package eventBus + +// Subscribe a channel to a topic and receive Events of that topic (the channel is only used to receive) +/* CAUTION : +-Channels are not resuable, they are closed when unsubscribing. +-Sending more events than are read can create massive memory leaks. +*/ + +// Credit : original code from : https://levelup.gitconnected.com/lets-write-a-simple-event-bus-in-go-79b9480d8997 (modified) + +import ( + "errors" + "sync" +) + +type DataChannel chan interface{} + +type DataChannelSlice []DataChannel + +// EventBus stores the information about subscribers interested in a particular topic +type EventBus struct { + subscribers map[string]DataChannelSlice //map[topic] (list of channels subscribed to the topic) + m sync.RWMutex +} + +func NewEventBus() EventBus { + return EventBus{ + subscribers: map[string]DataChannelSlice{}, + } +} + +// Publishes some data on some topic, data is sent to every subscriber of the topic +func (eb *EventBus) Publish(topic string, data interface{}) { + eb.m.RLock() + defer eb.m.RUnlock() + + if chans, found := eb.subscribers[topic]; found { + // this is done because the slices refer to same array even though they are passed by value + // thus we are creating a new slice with our elements thus preserve locking correctly. + + // Sends data to every channels subscribed to the topic + //(goroutine to avoid waiting for the previous subscriber to read the event to send the next and to avoid deadlock if the publisher and and the receiver are on the same goroutine) + channels := append(DataChannelSlice{}, chans...) + for _, ch := range channels { + go func(data interface{}, Channel DataChannel) { + /*defer func() { // Avoids panicking when the channel was closed (by unsubscribing) before the send was completed + recover() + }()*/ + Channel <- data + }(data, ch) + } + } +} + +func (eb *EventBus) Subscribe(topic string, ch DataChannel) { + eb.m.Lock() + defer eb.m.Unlock() + + if prev, found := eb.subscribers[topic]; found { + eb.subscribers[topic] = append(prev, ch) + } else { + eb.subscribers[topic] = append([]DataChannel{}, ch) + } +} + +func (eb *EventBus) UnSubscribe(topic string, ch DataChannel) error { + eb.m.Lock() + defer eb.m.Unlock() + + // Deletes the channel from the slice + if prev, found := eb.subscribers[topic]; found { + for i, a := range eb.subscribers[topic] { + if a == ch { + //close(ch) + + // Deletes channel from subscribers to the topic in an efficient way (avoids moving remaining data). + last := len(prev) - 1 + prev[i], prev[last] = prev[last], prev[i] + prev = prev[:last] + + eb.subscribers[topic] = prev + return nil + } + } + return errors.New(" this channel was not subscribed to this topic : " + topic) + } + return errors.New(" topic not found : " + topic) +} diff --git a/event_bus/event_bus_test.go b/event_bus/event_bus_test.go new file mode 100644 index 0000000..f7fe0b0 --- /dev/null +++ b/event_bus/event_bus_test.go @@ -0,0 +1,117 @@ +package eventBus + +import ( + "fmt" + "testing" + "time" +) + +func printDataEvent(ch string, data interface{}) { + fmt.Printf("Channel: %s; DataEvent: %v\n", ch, data) +} + +func TestFeed_simple(t *testing.T) { + var eb = NewEventBus() + + ch1 := make(chan interface{}) + ch2 := make(chan interface{}) + ch3 := make(chan interface{}) + + eb.Subscribe("topic1", ch1) + eb.Subscribe("topic2", ch2) + eb.Subscribe("topic3", ch3) + + go eb.Publish("topic1", "Hi topic 1") + go eb.Publish("topic2", "Welcome to topic 2") + go eb.Publish("topic3", "Welcome to topic 3") + + for i := 0; i < 3; i++ { + select { + case d := <-ch1: + printDataEvent("ch1", d) + case d := <-ch2: + printDataEvent("ch2", d) + case d := <-ch3: + printDataEvent("ch3", d) + } + } +} + +func TestFeed_UnSubscribe(t *testing.T) { + var eb = NewEventBus() + + ch1 := make(chan interface{}) + + eb.Subscribe("topic1", ch1) + + fmt.Println(eb.subscribers["topic1"]) + + eb.Publish("topic1", "Hi topic 1") + d, ok := <-ch1 + if !ok { + t.Errorf("Channel closed when it shouldn't.") + } + printDataEvent("ch1", d) + + err := eb.UnSubscribe("false_topic", ch1) + fmt.Println(err) + fmt.Println(eb.subscribers["topic1"]) + if err == nil { + t.Errorf("This should have produced an error.") + } + + err = nil + err = eb.UnSubscribe("topic1", ch1) + fmt.Println(err) + fmt.Println(eb.subscribers["topic1"]) + if err != nil { + t.Errorf("This should not have produced an error.") + } + + err = nil + err = eb.UnSubscribe("topic1", ch1) + fmt.Println(err) + fmt.Println(eb.subscribers["topic1"]) + if err == nil { + t.Errorf("This should have produced an error.") + } +} + +func TestFeed_ManyMessages(t *testing.T) { + var eb = NewEventBus() + + ch1 := make(chan interface{}) + ch2 := make(chan interface{}) + ch3 := make(chan interface{}) + + eb.Subscribe("topic1", ch1) + eb.Subscribe("topic2", ch2) + eb.Subscribe("topic3", ch3) + + // Sender + go func() { + for { + eb.Publish("topic1", "topic 1") + eb.Publish("topic2", "topic 2") + eb.Publish("topic3", "topic 3") + } + }() + + timer := time.NewTimer(5 * time.Second) + + received := 0 +receive: + for { + select { + case <-ch1: + case <-ch2: + case <-ch3: + case <-timer.C: + break receive + } + // fmt.Println(runtime.NumGoroutine()) + received++ + } + + fmt.Println("Number of message received : ", received) +} diff --git a/file/connInfo.go b/file/connInfo.go new file mode 100644 index 0000000..c0c568d --- /dev/null +++ b/file/connInfo.go @@ -0,0 +1,379 @@ +package fileSync + +import ( + "fmt" + "math/rand" + "sync" + "time" +) + +/* +connInfo stands for Connection Information. It contains all the information about a connection linked to a specific file. +The amount of requests and data we can ask at the same time is stored in it. +It is separated in levels. +*/ + +// ConnInfo ... +type ConnInfo interface { + // decrease the level of the connection (the number of requests we can send to him at the same time) + DecreaseLevel() + // when we had a decrease, it wait a certain time before retrying to send requests to him + ReAskAfterDecrease() + // when we had a decrease, it wait a certain time before retrying to increase the level of the connection + RetryToIncrease(currentIncreaseTime int64, currentIncreaseInc int) + // piece = true if we download piece by piece + UpdateNbAnswer(piece bool) + GetInfoRate() (bool, int) + // ad the number of bytes we received from this connection + AddBytesSeries(bytes int) + GetRate() int + GetNbRequestedPieces() int + GetAvailable() (bool, int, int64) + // reset also the timestamp of the last time the conn was available + SetAvailable(available bool) + RemovePieceRequested(pieceId int) + AddPieceRequested(pieceId int) bool + AddRequestBlock() bool + RemoveRequestBlock() + CanRequestBlock() bool + CanRequestPiece() bool + HavePiece(pieceId int) bool + // the node has a new piece. We add it to the bitfield + AddPiece(pieceId int) + IsReady() bool + SetBitfield(bitfield []bool) + GetIncreaseTime() int64 + String() string + GetPiecesRequested() []int + HadARecentDecrease() bool + GetNbRequests() int + GetLeecher() *FileLeecher + GetConn() ShosetConn +} + +// information about a connection +type ConnectionInformation struct { + Conn ShosetConn // connection + Leecher *FileLeecher // leecher + piecesRequested map[int]bool // list of pieces we have requested to him + bitfield []bool // bitfield of the pieces he has + nbBlocks int // number of blocks in a piece + askingEntirePieces bool // true if we are asking diretly entire pieces to him (no blocks separation) + nbMaxRequests int // number of requests can send to him at the same time + nbRequests int // number of requests we have sent to him + nbAnsweredRequests int // number of requests which have been answered + timestampSeries int64 // timestamp of the first request to calculate the speed + totalBytesSeries int // total bytes received in the series + available bool // true if we are authorised to send requests to him + availableTime int64 // timestamp of the last time the conn was available + availableInc int // this number grow higher if the peer is not available + increase bool // true if we can increase the number of requests + increaseTime int64 // timestamp of the last time we increased the number of requests + increaseInc int // this number grow higher if we can't increase the number of requests + + lastRequestTime int64 // timestamp of the last request we have sent to him + lastIncreaseTime int64 // timestamp of the last time we increased the number of requests + + m sync.RWMutex +} + +func NewConnInfo(conn ShosetConn, leecher *FileLeecher, blocks int) *ConnectionInformation { + return &ConnectionInformation{ + Conn: conn, + Leecher: leecher, + nbMaxRequests: 4, + nbBlocks: blocks, + piecesRequested: make(map[int]bool), + increase: true, + available: true, + increaseInc: 100, + availableInc: 100, + } +} + +// increase the lebel of the connection (the number of requests we can send to him at the same time) +func (connInfo *ConnectionInformation) increaseLevel() { + if connInfo.increase && time.Now().UnixMilli()-connInfo.lastIncreaseTime > 3000 { // we don't want to increase too fast + if connInfo.askingEntirePieces { + connInfo.nbMaxRequests = Min(connInfo.nbMaxRequests*2, 16) // max 16 requests + } else { + if connInfo.nbMaxRequests >= connInfo.nbBlocks { // if we reached the limit of blocks + connInfo.askingEntirePieces = true + connInfo.nbMaxRequests = 1 + } else { + connInfo.nbMaxRequests = connInfo.nbMaxRequests * 2 + } + } + connInfo.nbAnsweredRequests = 0 + connInfo.GetConn().GetLogger().Info().Msgf("increase level to : download piece by piece : %t with max %d requests.", connInfo.askingEntirePieces, connInfo.nbMaxRequests) + connInfo.lastIncreaseTime = time.Now().UnixMilli() + } +} + +// decrease the level of the connection (the number of requests we can send to him at the same time) +func (connInfo *ConnectionInformation) DecreaseLevel() { + connInfo.m.Lock() + defer connInfo.m.Unlock() + // we don't want to decrease too fast + if time.Now().UnixMilli()-connInfo.increaseTime > TIMEBEFOREANOTHERDECREASE && time.Now().UnixMilli()-connInfo.lastRequestTime < TIMEBEFOREANOTHERDECREASE { // if the inc have not been increased for the last ...s + conn := connInfo.Conn + connInfo.GetConn().GetLogger().Info().Msgf("from %v to %v decrease level from : download piece by piece : %t with max %d requests.", conn.GetLocalAddress(), conn.GetRemoteAddress(), connInfo.askingEntirePieces, connInfo.nbMaxRequests) + connInfo.increase = false + if connInfo.askingEntirePieces { + if connInfo.nbMaxRequests == 1 { // if we reached the limit of pieces + connInfo.askingEntirePieces = false + connInfo.nbMaxRequests = connInfo.nbBlocks / 2 + } else { + connInfo.nbMaxRequests = connInfo.nbMaxRequests / 2 + } + } else { + connInfo.nbMaxRequests = Max(connInfo.nbMaxRequests/2, 2) + } + connInfo.nbAnsweredRequests = 0 + + connInfo.increaseInc = Min(10000, connInfo.increaseInc*10) // max 10s x 10 + connInfo.increaseTime = time.Now().UnixMilli() + + // we authorise to increase the level of the connection after a certain time + go connInfo.RetryToIncrease(connInfo.increaseTime, connInfo.increaseInc) + + if connInfo.available { // if the conn is available + connInfo.available = false + // we authorise to send requests to him after a certain time + go connInfo.ReAskAfterDecrease() + } + } +} + +// when we had a decrease, it wait a certain time before retrying to send requests to him +func (connInfo *ConnectionInformation) ReAskAfterDecrease() { + connInfo.m.Lock() + conn := connInfo.Conn + connInfo.GetConn().GetLogger().Info().Msg(conn.GetLocalAddress() + " is sleeping 3 seconds before downloading again from " + conn.GetRemoteAddress()) + time.Sleep(time.Duration(TIMENOTASKINGDURINGDECREASE) * time.Millisecond) + connInfo.m.Unlock() + connInfo.SetAvailable(true) + connInfo.m.Lock() + connInfo.GetConn().GetLogger().Info().Msgf(conn.GetLocalAddress()+": sleeping finished, downloading again from : download piece by piece : %t with max %d requests.", connInfo.askingEntirePieces, connInfo.nbMaxRequests) + connInfo.m.Unlock() + connInfo.Leecher.DownloadNextPieces(conn) +} + +// when we had a decrease, it wait a certain time before retrying to increase the level of the connection +func (connInfo *ConnectionInformation) RetryToIncrease(currentIncreaseTime int64, currentIncreaseInc int) { + time.Sleep(time.Duration(currentIncreaseInc*(rand.Intn(10)+1)) * time.Millisecond) // we sleep the number of time we should + connInfo.m.Lock() + defer connInfo.m.Unlock() + if currentIncreaseTime == connInfo.increaseTime { // if we didn't received another decrease order while we were sleeping + connInfo.increase = true + go connInfo.resetIncreaseInc() + } +} + +func (connInfo *ConnectionInformation) resetIncreaseInc() { + notBreaking := true + for notBreaking { + connInfo.m.Lock() + if time.Now().UnixMilli()-connInfo.increaseTime > 6000 { // if we can still increase : everything back to normal : we reset the increase inc + connInfo.increaseInc = Max(100, connInfo.increaseInc/10) + } else { + notBreaking = false + } + connInfo.m.Unlock() + time.Sleep(5 * time.Second) + } +} + +// piece = true if we download piece by piece +func (connInfo *ConnectionInformation) UpdateNbAnswer(piece bool) { + connInfo.m.Lock() + defer connInfo.m.Unlock() + if (connInfo.askingEntirePieces && piece) || (!connInfo.askingEntirePieces && !piece) { + connInfo.nbAnsweredRequests++ + connInfo.nbRequests = Max(connInfo.nbRequests-1, 0) + } else { // if the level has changed recently, we don't want to increase the number of answered requests + connInfo.nbRequests = Max(connInfo.nbRequests-1, 0) + } + if connInfo.nbAnsweredRequests >= connInfo.nbMaxRequests { + connInfo.increaseLevel() + } +} + +func (connInfo *ConnectionInformation) GetInfoRate() (bool, int) { + connInfo.m.RLock() + defer connInfo.m.RUnlock() + return connInfo.askingEntirePieces, connInfo.nbMaxRequests +} + +// ad the number of bytes we received from this connection +func (connInfo *ConnectionInformation) AddBytesSeries(bytes int) { + connInfo.m.Lock() + defer connInfo.m.Unlock() + connInfo.totalBytesSeries += bytes +} + +func (connInfo *ConnectionInformation) resetRate() { + connInfo.timestampSeries = time.Now().UnixMilli() + connInfo.totalBytesSeries = 0 +} + +func (connInfo *ConnectionInformation) GetRate() int { + connInfo.m.Lock() + defer connInfo.m.Unlock() + deltaTime := Max64(time.Now().UnixMilli()-connInfo.timestampSeries, 1) // to not divide by 0 + rate := int(int64(connInfo.totalBytesSeries*1000) / deltaTime) // bytes per second + connInfo.resetRate() + return rate +} + +func (connInfo *ConnectionInformation) GetNbRequestedPieces() int { + connInfo.m.RLock() + defer connInfo.m.RUnlock() + return len(connInfo.piecesRequested) +} + +func (connInfo *ConnectionInformation) GetAvailable() (bool, int, int64) { + connInfo.m.RLock() + defer connInfo.m.RUnlock() + return connInfo.available, connInfo.availableInc, connInfo.availableTime +} + +// reset also the timestamp of the last time the conn was available +func (connInfo *ConnectionInformation) SetAvailable(available bool) { + connInfo.m.Lock() + defer connInfo.m.Unlock() + connInfo.available = available + if !available { + connInfo.availableTime = 0 + connInfo.availableInc = Min(100000, connInfo.availableInc*10) + } else { + connInfo.availableInc = 1 + connInfo.availableTime = time.Now().UnixMilli() + } +} + +func (connInfo *ConnectionInformation) RemovePieceRequested(pieceId int) { + connInfo.m.Lock() + defer connInfo.m.Unlock() + delete(connInfo.piecesRequested, pieceId) + connInfo.nbRequests = Max(connInfo.nbRequests-1, 0) +} + +func (connInfo *ConnectionInformation) AddPieceRequested(pieceId int) bool { + connInfo.m.Lock() + defer connInfo.m.Unlock() + if connInfo.available { + if connInfo.askingEntirePieces { + if connInfo.nbRequests < connInfo.nbMaxRequests { + connInfo.piecesRequested[pieceId] = true + connInfo.lastRequestTime = time.Now().UnixMilli() + connInfo.nbRequests++ + return true + } + } else { + if connInfo.nbRequests < connInfo.nbMaxRequests && len(connInfo.piecesRequested) <= 1 { + connInfo.piecesRequested[pieceId] = true + return true + } + } + } + return false +} + +func (connInfo *ConnectionInformation) AddRequestBlock() bool { + connInfo.m.Lock() + defer connInfo.m.Unlock() + if !connInfo.askingEntirePieces && connInfo.available && connInfo.nbRequests < connInfo.nbMaxRequests { + connInfo.nbRequests++ + connInfo.lastRequestTime = time.Now().UnixMilli() + return true + } + return false +} + +func (connInfo *ConnectionInformation) RemoveRequestBlock() { + connInfo.m.Lock() + defer connInfo.m.Unlock() + connInfo.nbRequests = Max(connInfo.nbRequests-1, 0) +} + +func (connInfo *ConnectionInformation) CanRequestBlock() bool { + connInfo.m.RLock() + defer connInfo.m.RUnlock() + return connInfo.available && (connInfo.nbRequests < connInfo.nbMaxRequests || connInfo.askingEntirePieces) +} + +func (connInfo *ConnectionInformation) CanRequestPiece() bool { + connInfo.m.RLock() + defer connInfo.m.RUnlock() + return connInfo.available && connInfo.askingEntirePieces && connInfo.nbRequests < connInfo.nbMaxRequests +} + +func (connInfo *ConnectionInformation) HavePiece(pieceId int) bool { + connInfo.m.RLock() + defer connInfo.m.RUnlock() + return connInfo.bitfield[pieceId] +} + +// the node has a new piece. We add it to the bitfield +func (connInfo *ConnectionInformation) AddPiece(pieceId int) { + connInfo.m.Lock() + defer connInfo.m.Unlock() + connInfo.bitfield[pieceId] = true +} + +func (connInfo *ConnectionInformation) IsReady() bool { + connInfo.m.RLock() + defer connInfo.m.RUnlock() + return len(connInfo.bitfield) > 0 +} + +func (connInfo *ConnectionInformation) SetBitfield(bitfield []bool) { + connInfo.m.Lock() + defer connInfo.m.Unlock() + connInfo.bitfield = bitfield +} + +func (connInfo *ConnectionInformation) GetIncreaseTime() int64 { + connInfo.m.RLock() + defer connInfo.m.RUnlock() + return connInfo.increaseTime +} + +func (connInfo *ConnectionInformation) String() string { + connInfo.m.RLock() + defer connInfo.m.RUnlock() + return fmt.Sprintf("askingEntirePieces : %t, nbMaxRequests : %v, nbRequests : %v, nbAnsweredRequests : %v, increase : %v, increaseTime : %v, increaseInc : %v, available : %v, availableTime : %v, availableInc : %v, piecesRequested : %v", connInfo.askingEntirePieces, connInfo.nbMaxRequests, connInfo.nbRequests, connInfo.nbAnsweredRequests, connInfo.increase, connInfo.increaseTime, connInfo.increaseInc, connInfo.available, connInfo.availableTime, connInfo.availableInc, connInfo.piecesRequested) +} + +func (connInfo *ConnectionInformation) GetPiecesRequested() []int { + connInfo.m.RLock() + defer connInfo.m.RUnlock() + pieces := []int{} + connInfo.GetConn().GetLogger().Info().Msgf("requested", connInfo.piecesRequested) + for pieceId := range connInfo.piecesRequested { + pieces = append(pieces, pieceId) + } + return pieces +} + +func (connInfo *ConnectionInformation) HadARecentDecrease() bool { + connInfo.m.RLock() + defer connInfo.m.RUnlock() + // true if the last decrease was less than 3 seconds ago or if the number of requests is less than half of the max number of requests + return time.Now().UnixMilli()-connInfo.lastRequestTime > 3000 +} + +func (connInfo *ConnectionInformation) GetNbRequests() int { + connInfo.m.RLock() + defer connInfo.m.RUnlock() + return connInfo.nbRequests +} + +func (connInfo *ConnectionInformation) GetLeecher() *FileLeecher { + return connInfo.Leecher +} + +func (connInfo *ConnectionInformation) GetConn() ShosetConn { + return connInfo.Conn +} diff --git a/file/const.go b/file/const.go new file mode 100644 index 0000000..51ad451 --- /dev/null +++ b/file/const.go @@ -0,0 +1,21 @@ +package fileSync + +import ( + "io/fs" +) + +// file +const ( + CHUNKSIZE int = 16 * 1024 // 16Kbytes + CHUNKTIMEOUT int64 = 1 //s + PERMISSION fs.FileMode = 0776 // permission for file and folders + PATH_COPY_FILES string = ".copy" +) + +// Timeout Parameters +const ( + TIMEBEFOREANOTHERDECREASE int64 = 3000 //ms + TIMEOUTREQUEST int64 = 2000 //ms + TIMENOTASKINGDURINGDECREASE int64 = 3000 //ms + TIMEBEFOREOPERATION int64 = 1000 //ms +) diff --git a/file/externalCommands.go b/file/externalCommands.go new file mode 100644 index 0000000..e8c4832 --- /dev/null +++ b/file/externalCommands.go @@ -0,0 +1,234 @@ +package fileSync + +import ( + "fmt" + "io" + "os" + "path/filepath" + "sync" + "time" + + "github.com/ditrit/shoset/msg" + guuid "github.com/kjk/betterguid" +) + +/* +This package provide accessible commands to the user. +The commands are : move, delete, add, modify +Use these commands to modify a file in the library to do it safely. +It asks to the other node for their permission to modify the library. +*/ + +type ExternalCommands struct { + FileTransfer FileTransfer + locked bool + currentUUID string + addressMap map[string]bool + nbConn int + answerChan chan bool + + m sync.Mutex +} + +func NewExternalCommands(fileTransfer FileTransfer) *ExternalCommands { + ec := new(ExternalCommands) + ec.FileTransfer = fileTransfer + ec.addressMap = make(map[string]bool) + ec.answerChan = make(chan bool, 1) + + return ec +} + +// rename or move a file / folder inside the library +func (ec *ExternalCommands) Move(previousPath string, newPath string) error { + err := ec.VerifyPath(previousPath) + if err != nil { + return err + } + err = ec.VerifyPath(newPath) + if err != nil { + return err + } + err = ec.Lock() + defer ec.Unlock() + if err != nil { + return err + } + + err = ec.FileTransfer.GetLibrary().Move(previousPath, newPath) + return err +} + +// delete a file / folder from the library +func (ec *ExternalCommands) Delete(path string) error { + err := ec.VerifyPath(path) + if err != nil { + return err + } + err = ec.Lock() + defer ec.Unlock() + if err != nil { + return err + } + + err = ec.FileTransfer.GetLibrary().Remove(path) + return err +} + +// add a new file to the library +// path : full path of the file to add +// libraryPath : path of the file in the library +// if libraryPath does not exist, it will be created +func (ec *ExternalCommands) Add(path string, libraryPath string) error { + fmt.Println("add : ", path, " to ", libraryPath, " in the library") + file, err := os.Stat(path) + if err != nil { + return fmt.Errorf("can't add the file : %s", err) + } + if file.IsDir() { + return fmt.Errorf("can't add a directory") + } + _, err = os.Stat(libraryPath) + if err == nil { + return fmt.Errorf(libraryPath + " already exist in the library") + } + if !IsInDir(libraryPath, ec.FileTransfer.GetLibrary().GetDir()) { + return fmt.Errorf(path + " is not a path in the library") + } + + err = ec.Lock() + defer ec.Unlock() + if err != nil { + return err + } + // create the libraryPath if it does not exist + directory := filepath.Dir(libraryPath) + err = os.MkdirAll(directory, os.ModePerm) + if err != nil { + return err + } + libraryPathFile, err := os.Create(libraryPath) + if err != nil { + return err + } + pathFile, err := os.Open(path) + if err != nil { + + return err + } + _, err = io.Copy(libraryPathFile, pathFile) + if err != nil { + return err + } + err = ec.FileTransfer.GetLibrary().Add(libraryPath) + return err +} + +// modify a file in the library +// it replace the file content in the library by the file in the path +func (ec *ExternalCommands) Modify(path string, libraryPath string) error { + pathInfo, err := os.Stat(path) + if err != nil { + return err + } + if pathInfo.IsDir() { + return fmt.Errorf(path + " is a directory, you can't 'modify' a directory") + } + err = ec.VerifyPath(libraryPath) + if err != nil { + return err + } + err = ec.Lock() + defer ec.Unlock() + if err != nil { + return err + } + + err = ec.FileTransfer.GetLibrary().Modify(libraryPath) + return err +} + +func (ec *ExternalCommands) Lock() error { + ec.m.Lock() + ec.currentUUID = guuid.New() + askLocked := msg.FileMessage{ + MessageName: "askLibraryLocked", + FileUUID: ec.currentUUID, + } + askLocked.InitMessageBase() + ec.FileTransfer.Broadcast(&askLocked) + ec.m.Unlock() + err := ec.WaitAnswer() + return err +} + +func (ec *ExternalCommands) WaitAnswer() error { + select { + case answer := <-ec.answerChan: + ec.m.Lock() + defer ec.m.Unlock() + ec.locked = answer + if !answer { + err := fmt.Errorf("library is locked because : some nodes have already locked their library : %T", ec.addressMap) + ec.addressMap = make(map[string]bool) + return err + } else { + ec.addressMap = make(map[string]bool) + return nil + } + case <-time.After(5 * time.Second): + return fmt.Errorf("library is locked because : timeout") + } +} + +func (ec *ExternalCommands) Unlock() { + ec.m.Lock() + defer ec.m.Unlock() + ec.locked = false +} + +func (ec *ExternalCommands) VerifyPath(path string) error { + _, err := os.Stat(path) + if err != nil { + return err + } + if !IsInDir(path, ec.FileTransfer.GetLibrary().GetDir()) { + return fmt.Errorf(path + " is not a path in the library") + } + return nil +} + +func (ec *ExternalCommands) ReceiveAnswer(answer *msg.FileMessage, address string, nbConn int) { + if answer.FileUUID == ec.currentUUID { + ec.m.Lock() + ec.nbConn = nbConn + ec.addressMap[address] = answer.AnswerLocked + if len(ec.addressMap) == ec.nbConn { + //fmt.Println("we have all the answers for locking: ", ec.addressMap) + answer := true + for _, v := range ec.addressMap { + if v { // if someone answered that it was locked + answer = false + ec.answerChan <- false + break + } + } + if answer { + ec.answerChan <- true + } + } + ec.m.Unlock() + } +} + +func (ec *ExternalCommands) IsLocked(ask *msg.FileMessage) msg.FileMessage { + ec.m.Lock() + defer ec.m.Unlock() + answer := msg.FileMessage{ + MessageName: "answerLibraryLocked", + AnswerLocked: ec.locked, + FileUUID: ask.FileUUID, + } + answer.InitMessageBase() + return answer +} diff --git a/file/file.go b/file/file.go new file mode 100644 index 0000000..05fcfb2 --- /dev/null +++ b/file/file.go @@ -0,0 +1,517 @@ +package fileSync + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "io" + "log" + "math" + "os" + "path/filepath" + "strconv" + "sync" + "time" +) + +/* +This is used to do operations on one file (read, write data, calculate hash of the file, have the version, ...). +Each file in the library and in the copy of the library has a file instance. +*/ + +type File interface { + LoadFromMap(fileInfoMap map[string]interface{}) + GetFileInfoMap() map[string]interface{} + OpenFile() (*os.File, error) + CloseFile() + // write a chunk at the specified location + WriteChunk(chunk []byte, offset int64) error + String() string + // Return the chunk of data at the specified index + LoadData(chunk int64, chunkSize int) ([]byte, error) + CalculateHashMap() (map[int]string, error) + CalculateHash() (string, error) + // update all the information about the file from what is on the disk + UpdateMetadata() error + // move the file to a new location (and / or rename it) + Move(newRelativePath string, newName string) error + GetName() string + GetSize() int64 + GetHash() string + GetHashMap() map[int]string + GetHashChunk(index int) string + GetPieceSize() int + GetVersion() int + SetVersion(version int) + GetRelativePath() string + SetRelativePath(relativePath string) + SetName(name string) + SetHash(hash string) + SetHashMap(hashMap map[int]string) +} + +type FileImpl struct { + name string + baseFilesDir string // path to the directory where all the files are stored + relativePath string // path to the file relative to the baseFilesDir + size int64 // size in bytes of the file + pieceSize int // size in bytes of a piece of the file + nbPieces int // number of pieces of the file + hash string + hashMap map[int]string + version int // version of the file + + openedFile *os.File + openedTime int64 + closingGoRoutine bool + + m sync.RWMutex +} + +// We have a new file coming from an other node : it creates a new file +func NewEmptyFile(baseFilesDir string, relativePath string, name string, size int64, hash string, version int, hashMap map[int]string) (*FileImpl, error) { + file := FileImpl{} + file.m.Lock() + defer file.m.Unlock() + + file.baseFilesDir = baseFilesDir + file.relativePath = relativePath + file.name = name + file.size = size + file.pieceSize = CalculatePieceSize(size) + file.hashMap = make(map[int]string, file.pieceSize) + file.nbPieces = int(math.Ceil(float64(file.size) / float64(file.pieceSize))) + if len(hashMap) != file.nbPieces { // there is a problem in the way the hashMap is given + return &file, fmt.Errorf("the hashMap has a size of " + strconv.Itoa(len(hashMap)) + " whereas the file has " + strconv.Itoa(file.nbPieces) + " pieces") + } + file.version = version + file.hash = hash + + for i := 0; i < file.nbPieces; i++ { + file.hashMap[i] = hashMap[i] + } + + // create the directory in the relativePath if it doesn't exist + err := os.MkdirAll(filepath.Join(baseFilesDir, file.relativePath), PERMISSION) + if err != nil && !os.IsExist(err) { + return &file, err + } + // create the file in the relative directory + fc, err := os.Create(filepath.Join(baseFilesDir, file.relativePath, file.name)) + if err != nil { // if there is a problem with the file creation + return &file, err + } + fc.Close() + // create the file and fill it with empty bytes (in the .copy directory) + fd, err := os.Create(filepath.Join(baseFilesDir, file.relativePath, file.name)) + if err != nil { // if there is a problem with the file creation + return &file, err + } + _, err = fd.Seek(int64(size-1), 0) + if err != nil { + return &file, err + } + _, err = fd.Write([]byte{0}) + if err != nil { + return &file, err + } + err = fd.Close() + if err != nil { + return &file, err + } + return &file, err +} + +// We load a file (if it doesn't exist, we create it) and load the metadata from it (size, hash, hashMap, ...) +func LoadFile(baseFilesDir string, relativePath string, name string) (*FileImpl, error) { + file := FileImpl{} + file.m.Lock() + file.baseFilesDir = baseFilesDir + file.relativePath = relativePath + file.name = name + file.version = 0 + file.m.Unlock() + // we create the file if it doesn't exist + _, err := os.Stat(filepath.Join(baseFilesDir, relativePath, name)) + if err != nil { + if os.IsNotExist(err) { + // create the directory in the relativePath if it doesn't exist + err := os.MkdirAll(filepath.Join(baseFilesDir, relativePath), PERMISSION) + if err != nil && !os.IsExist(err) { + return &file, err + } + // create the file in the relative directory + fc, err := os.Create(filepath.Join(baseFilesDir, relativePath, name)) + if err != nil { // if there is a problem with the file creation + return &file, err + } + fc.Close() + } + } + err = file.UpdateMetadata() + return &file, err +} + +// Load the file information from the info on the map and detect differences with the actual file +// it is used when we launch the node to detect if some files have been modified accidentally +func LoadFileInfo(fileInfoMap map[string]interface{}) (*FileImpl, error) { + file := FileImpl{} + + file.LoadFromMap(fileInfoMap) + + file.m.Lock() + defer file.m.Unlock() + newHash, err := file.calculateHash() + if err != nil { + return nil, err + } + if newHash != file.hash { + // the file in the .copy has probably been modified + // it occurs when the node is closed while a file is being downloaded + file.version = 0 + return nil, fmt.Errorf("the file has been modified, hashes are different") + } + return &file, nil +} + +func (file *FileImpl) LoadFromMap(fileInfoMap map[string]interface{}) { + file.m.Lock() + defer file.m.Unlock() + + file.baseFilesDir = fileInfoMap["baseFilesDir"].(string) + file.relativePath = fileInfoMap["relativePath"].(string) + file.name = fileInfoMap["name"].(string) + file.size = int64(fileInfoMap["size"].(float64)) + file.pieceSize = int(fileInfoMap["pieceSize"].(float64)) + file.nbPieces = int(fileInfoMap["nbPieces"].(float64)) + file.hash = fileInfoMap["hash"].(string) + newMap := fileInfoMap["hashMap"].(map[string]interface{}) + for i, hash := range newMap { + stri, _ := strconv.Atoi(i) + file.hashMap[stri] = hash.(string) + } + file.version = int(fileInfoMap["version"].(float64)) +} + +func (file *FileImpl) GetFileInfoMap() map[string]interface{} { + file.m.RLock() + defer file.m.RUnlock() + fileInfo := make(map[string]interface{}) + fileInfo["name"] = file.name + fileInfo["baseFilesDir"] = file.baseFilesDir + fileInfo["relativePath"] = file.relativePath + fileInfo["size"] = file.size + fileInfo["pieceSize"] = file.pieceSize + fileInfo["nbPieces"] = file.nbPieces + fileInfo["hash"] = file.hash + newHashMap := make(map[int]string) + for i := 0; i < file.nbPieces; i++ { + newHashMap[i] = file.hashMap[i] + } + fileInfo["hashMap"] = newHashMap + fileInfo["version"] = file.version + return fileInfo +} + +func (file *FileImpl) openFile() (*os.File, error) { + if file.openedFile == nil { // if we don't have already opened the file + path := filepath.Join(file.baseFilesDir, file.relativePath, file.name) + openedFile, err := os.OpenFile(path, os.O_RDWR, PERMISSION) + if err != nil { + return nil, err + } + file.openedFile = openedFile + file.openedTime = time.Now().Unix() + if !file.closingGoRoutine { + go file.closeFileAfterTimeout() + } + } + return file.openedFile, nil +} + +// to execute in a gori=outine : close the file after 5s if nobody have written or read from the file +func (file *FileImpl) closeFileAfterTimeout() { + for { + file.m.Lock() + file.closingGoRoutine = true + if file.openedFile != nil && (time.Now().Unix()-file.openedTime) > 5 { // if the file has been opened for more than 5s and nobody have made a request for a chunk + file.closeFile() + file.closingGoRoutine = false + file.m.Unlock() + break + } + file.m.Unlock() + time.Sleep(1 * time.Second) + } +} + +func (file *FileImpl) OpenFile() (*os.File, error) { + file.m.Lock() + defer file.m.Unlock() + return file.openFile() +} + +func (file *FileImpl) closeFile() { + if file.openedFile != nil { + file.openedFile.Close() + file.openedFile = nil + } +} + +func (file *FileImpl) CloseFile() { + file.m.Lock() + defer file.m.Unlock() + file.closeFile() +} + +// write a chunk at the specified location +func (file *FileImpl) WriteChunk(chunk []byte, offset int64) error { + file.m.Lock() + defer file.m.Unlock() + openedFile, err := file.openFile() + if err != nil { + return err + } + + n, err := openedFile.WriteAt(chunk, offset) + if err != nil { + return err + } + if n != len(chunk) { + return fmt.Errorf("error while writing chunk") + } + return nil +} + +func (file *FileImpl) String() string { + file.m.RLock() + defer file.m.RUnlock() + var result string + result += "Name (file) : " + file.name + "\n" + result += "Path of the library : " + file.baseFilesDir + "\n" + result += "RelativePath : " + file.relativePath + "\n" + result += "Size: " + fmt.Sprint(file.size) + "\n" + + return result +} + +// Return the chunk of data at the specified index +func (file *FileImpl) LoadData(chunk int64, chunkSize int) ([]byte, error) { + file.m.Lock() + defer file.m.Unlock() + //fmt.Println("LoadData", chunk, chunkSize) + return file.loadData(chunk, chunkSize) +} + +// Return the chunk of data at the specified index +func (file *FileImpl) loadData(begin int64, size int) ([]byte, error) { + dataToRead := Min64(int64(size), file.size-begin) + if dataToRead <= 0 { + return []byte{}, nil + } + data := make([]byte, dataToRead) + openedFile, err := file.openFile() + if err != nil { + return data, err + } + + _, err = openedFile.ReadAt(data, int64(begin)) // read the file from the offset chunk*chunkSize + if err != nil && err != io.EOF { + return data, err + } + + return data, nil +} + +func (file *FileImpl) calculateHashMap() (map[int]string, error) { + // we do calculate hash chunk by chunk, add them together and then do a hash of the list of hash + // this way we can check the hash of a file without having to load it in memory + size, err := file.readSize() + if err != nil { + + return nil, err + } + nbPieces := int(size / int64(file.pieceSize)) + hashMap := make(map[int]string, nbPieces) + for i := 0; i < nbPieces; i++ { + chunk, err := file.loadData(int64(i)*int64(file.pieceSize), file.pieceSize) + if err != nil { + return nil, err + } + hashMap[i] = Hash(chunk) + } + return hashMap, nil +} + +func (file *FileImpl) CalculateHashMap() (map[int]string, error) { + file.m.Lock() + defer file.m.Unlock() + return file.calculateHashMap() +} + +func (file *FileImpl) calculateHash() (string, error) { + hashMap, err := file.calculateHashMap() + if err != nil { + return "", err + } + + hasher := sha256.New() + for i := 0; i < file.nbPieces; i++ { + hasher.Write([]byte(hashMap[i])) + } + //hasher.Write([]byte(file.name)) + //hasher.Write([]byte([]byte(strconv.Itoa(file.version)))) + hex.EncodeToString(hasher.Sum(nil)) + return hex.EncodeToString(hasher.Sum(nil)), nil +} + +func (file *FileImpl) CalculateHash() (string, error) { + file.m.Lock() + defer file.m.Unlock() + return file.calculateHash() +} + +// read the size of the file +func (file *FileImpl) readSize() (int64, error) { + info, err := os.Stat(filepath.Join(file.baseFilesDir, file.relativePath, file.name)) + if err != nil { + return 0, err + } + return info.Size(), nil +} + +// update all the information about the file from what is on the disk +func (file *FileImpl) UpdateMetadata() error { + file.m.Lock() + defer file.m.Unlock() + size, err := file.readSize() + if err != nil { + return err + } + file.size = size + file.pieceSize = CalculatePieceSize(size) + file.nbPieces = int(size / int64(file.pieceSize)) + file.hashMap = make(map[int]string, file.pieceSize) + file.nbPieces = int(math.Ceil(float64(file.size) / float64(file.pieceSize))) + + file.hashMap, err = file.calculateHashMap() + if err != nil { + return err + } + file.hash, err = file.calculateHash() + if err != nil { + return err + } + return nil +} + +// move the file to a new location (and / or rename it) +func (file *FileImpl) Move(newRelativePath string, newName string) error { + file.m.Lock() + defer file.m.Unlock() + if file.openedFile != nil { + file.openedFile.Close() + } + err := os.Rename(filepath.Join(file.baseFilesDir, file.relativePath, file.name), filepath.Join(file.baseFilesDir, newRelativePath, newName)) + if err != nil { + return err + } + file.relativePath = newRelativePath + file.name = newName + return nil +} + +/* +setters and getters +*/ + +func (file *FileImpl) GetName() string { + file.m.RLock() + defer file.m.RUnlock() + + return file.name +} + +func (file *FileImpl) GetSize() int64 { + file.m.RLock() + defer file.m.RUnlock() + + return file.size +} + +func (file *FileImpl) GetHash() string { + file.m.RLock() + defer file.m.RUnlock() + + return file.hash +} + +func (file *FileImpl) GetHashMap() map[int]string { + file.m.RLock() + defer file.m.RUnlock() + newHashMap := make(map[int]string) + for k, v := range file.hashMap { + newHashMap[k] = v + } + return newHashMap +} + +func (file *FileImpl) GetHashChunk(index int) string { + file.m.RLock() + defer file.m.RUnlock() + return file.hashMap[index] +} + +func (file *FileImpl) GetPieceSize() int { + file.m.RLock() + defer file.m.RUnlock() + return file.pieceSize +} + +func (file *FileImpl) GetVersion() int { + file.m.RLock() + defer file.m.RUnlock() + return file.version +} + +func (file *FileImpl) SetVersion(version int) { + file.m.Lock() + defer file.m.Unlock() + file.version = version +} + +func (file *FileImpl) GetRelativePath() string { + file.m.RLock() + defer file.m.RUnlock() + return file.relativePath +} + +func (file *FileImpl) SetRelativePath(relativePath string) { + file.m.Lock() + defer file.m.Unlock() + err := os.MkdirAll(filepath.Join(file.baseFilesDir, relativePath), 0755) + if err != nil { + log.Println("Error creating directory", err) + } + file.relativePath = relativePath +} + +func (file *FileImpl) SetName(name string) { + file.m.Lock() + defer file.m.Unlock() + file.name = name +} + +func (file *FileImpl) SetHash(hash string) { + file.m.Lock() + defer file.m.Unlock() + file.hash = hash +} + +func (file *FileImpl) SetHashMap(hashMap map[int]string) { + file.m.Lock() + defer file.m.Unlock() + for k, v := range hashMap { + file.hashMap[k] = v + } +} diff --git a/file/fileLeecher.go b/file/fileLeecher.go new file mode 100644 index 0000000..94d24e7 --- /dev/null +++ b/file/fileLeecher.go @@ -0,0 +1,669 @@ +package fileSync + +import ( + "fmt" + "math/rand" + "strconv" + "time" + + "github.com/ditrit/shoset/msg" +) + +/* +This file is used to describe a file leecher +a file leecher is a structure that download a file from a file seeder and share it at the same time +It inherits from the file seeder +This is inspired by the bittorrent protocol +*/ + +type FileLeecher struct { + FileSeeder + hash string // hash of the file we are downloading + hashMap map[int]string // map of the hash of the pieces of the file + nbPieces int // total number of pieces in the file + nbPiecesReceived int + pieceSize int // size (in bytes) of a piece + nbBlocks int // number of blocks per piece + receivedPieces []bool // pieces we have received + requestedPieces []bool // pieces we have requested + piecesBeingRequested map[int]*Piece // pieces that are being requested + pieceAskedToConn map[int]ConnInfo // map of the piece we have asked to a connection + piecesToRequestPriority map[int]bool // pieces that we want to request with top prioriyt because unfinished pieces. All these pieces are also in piecesBeingRequested + pieceRarity []int // rarity of the pieces (nb of peer that have the piece) + ConnInfoMap map[ShosetConn]ConnInfo // give us the info of a connection + GetBitfiledFile map[ShosetConn]bool // map of conn we have asked for bitfield + + stop chan bool // channel to stop the leecher + stopped bool // true if the leecher is stopped + startTimestamp int64 // timestamp of the start of the leecher +} + +func NewFileLeecher(syncFile SyncFile, fileTransfer FileTransfer) *FileLeecher { + var fileLeecher FileLeecher + fileLeecher.SyncFile = syncFile + fileLeecher.InitSeeder(syncFile, fileTransfer) + fileLeecher.hash = syncFile.GetCopyFile().GetHash() + fileLeecher.hashMap = syncFile.GetCopyFile().GetHashMap() + fileLeecher.pieceSize = fileLeecher.File.GetPieceSize() + fileLeecher.nbPieces = len(fileLeecher.hashMap) + fileLeecher.nbPiecesReceived = 0 + fileLeecher.nbBlocks = int(fileLeecher.File.GetPieceSize()) / CHUNKSIZE + + fileLeecher.receivedPieces = make([]bool, fileLeecher.nbPieces) + fileLeecher.requestedPieces = make([]bool, fileLeecher.nbPieces) + fileLeecher.piecesBeingRequested = make(map[int]*Piece) + fileLeecher.pieceAskedToConn = make(map[int]ConnInfo) + fileLeecher.piecesToRequestPriority = make(map[int]bool) + fileLeecher.pieceRarity = make([]int, fileLeecher.nbPieces) + + fileLeecher.stop = make(chan bool) + fileLeecher.ConnInfoMap = make(map[ShosetConn]ConnInfo) + fileLeecher.GetBitfiledFile = make(map[ShosetConn]bool) + + fileLeecher.startTimestamp = time.Now().UnixMilli() + + rand.Seed(time.Now().UnixNano()) + return &fileLeecher +} + +// a newer version has appeared during the download +func (fileLeecher *FileLeecher) UpdateLeeching() { + fileLeecher.m.Lock() + defer fileLeecher.m.Unlock() + + // update the receiveMap in a clever way + // we don't want to loose the pieces we have already received + newHash := fileLeecher.SyncFile.GetCopyFile().GetHash() + if newHash != fileLeecher.hash { + newHashMap := fileLeecher.SyncFile.GetCopyFile().GetHashMap() + newReceivedPieces := make([]bool, len(newHashMap)) + + for i, newHash := range newHashMap { + hash, ok := fileLeecher.hashMap[i] + if ok && hash == newHash { // if the same piece + newReceivedPieces[i] = fileLeecher.receivedPieces[i] + } + } + + fileLeecher.hashMap = newHashMap + fileLeecher.hash = newHash + fileLeecher.receivedPieces = newReceivedPieces + fileLeecher.nbPieces = len(newHashMap) + fileLeecher.nbPiecesReceived = 0 + fileLeecher.requestedPieces = make([]bool, fileLeecher.nbPieces) + fileLeecher.piecesBeingRequested = make(map[int]*Piece) + fileLeecher.pieceAskedToConn = make(map[int]ConnInfo) + fileLeecher.piecesToRequestPriority = make(map[int]bool) + fileLeecher.pieceRarity = make([]int, fileLeecher.nbPieces) + + fileLeecher.ConnInfoMap = make(map[ShosetConn]ConnInfo) + fileLeecher.GetBitfiledFile = make(map[ShosetConn]bool) + } +} + +func (fileLeecher *FileLeecher) SendHaveMessage(pieceId int) { + haveMessage := msg.FileMessage{ + MessageName: "have", + FileUUID: fileLeecher.SyncFile.GetUUID(), + FileName: fileLeecher.File.GetName(), + FileHash: fileLeecher.File.GetHash(), + PieceNumber: pieceId, + } + haveMessage.InitMessageBase() + fileLeecher.InterestedConn.Range(func(key, value interface{}) bool { + conn := key.(ShosetConn) + if value.(bool) { + fileLeecher.FileTransfer.SendMessage(haveMessage, conn) + } + return true + }) +} + +func (fileLeecher *FileLeecher) ReceiveBitfieldMessage(conn ShosetConn, bitfield []bool) { + fileLeecher.m.Lock() + connInfo, ok := fileLeecher.ConnInfoMap[conn] + fileLeecher.m.Unlock() + if !ok { // new connection + fileLeecher.InitDownload(conn) + return + } + if connInfo.IsReady() { // if we already have the bitfield + return + } + fileLeecher.m.Lock() + delete(fileLeecher.GetBitfiledFile, conn) + if bitfield == nil { + bitfield = make([]bool, fileLeecher.nbPieces) + } + connInfo.SetBitfield(bitfield) + for i, piece := range bitfield { + if piece { + fileLeecher.pieceRarity[i] += 1 + } + } + fileLeecher.m.Unlock() + fileLeecher.DownloadNextPieces(conn) +} + +func (fileLeecher *FileLeecher) ReceiveHaveMessage(conn ShosetConn, pieceId int) { + fileLeecher.m.Lock() + connInfo, ok := fileLeecher.ConnInfoMap[conn] + if !ok || !connInfo.IsReady() { // new connection + fileLeecher.launchDownload(conn) + fileLeecher.m.Unlock() + return + } + if !connInfo.HavePiece(pieceId) && !fileLeecher.receivedPieces[pieceId] { // if we did'nt know he had this piece and we don't have it + connInfo.AddPiece(pieceId) + fileLeecher.pieceRarity[pieceId]++ + // we can ask for this piece + fileLeecher.m.Unlock() + fileLeecher.DownloadNextPieces(conn) + } else { + fileLeecher.m.Unlock() + } +} + +// we remove a conn from the authorised conn for a certain time +func (fileLeecher *FileLeecher) removeConn(conn ShosetConn) { + conn.GetLogger().Info().Msg("------- RemoveConn ------- from " + conn.GetLocalAddress() + " to " + conn.GetRemoteAddress()) + + connInfo, ok := fileLeecher.ConnInfoMap[conn] + if !ok { + return + } + available, _, _ := connInfo.GetAvailable() + if available { + connInfo.SetAvailable(false) + + for _, pieceId := range connInfo.GetPiecesRequested() { + fileLeecher.piecesToRequestPriority[pieceId] = true + connInfo.RemovePieceRequested(pieceId) + } + + _, inc, _ := connInfo.GetAvailable() + go func() { + conn.GetLogger().Info().Msgf("sleeping %d milliseconds before downloading again", inc) + time.Sleep(time.Duration(inc*(rand.Intn(10)+1)) * time.Millisecond) + connInfo.SetAvailable(true) + conn.GetLogger().Info().Msg("sleeping finished, downloading again") + fileLeecher.DownloadNextPieces(conn) + }() + } +} + +func (fileLeecher *FileLeecher) ReceiveChunk(conn ShosetConn, begin int64, length int, block []byte) error { + fileLeecher.m.Lock() + if fileLeecher.stopped { + fileLeecher.m.Unlock() + return fmt.Errorf("file leecher is stopped") + } + // retrieve the connection info + connInfo, ok := fileLeecher.ConnInfoMap[conn] + if !ok { // new connection + fileLeecher.launchDownload(conn) + fileLeecher.m.Unlock() + return fmt.Errorf("connection %s is not in the map", conn.GetRemoteAddress()) + } + // retrieve the piece + pieceId := int(begin / int64(fileLeecher.pieceSize)) + piece, ok := fileLeecher.piecesBeingRequested[pieceId] + if !ok { // this piece is not currently being downloaded + fileLeecher.m.Unlock() + return fmt.Errorf("piece %d is not currently being downloaded", pieceId) + } + + connInfo.AddBytesSeries(len(block)) + + fileLeecher.FileTransfer.DecreaseMissingLength(len(block)) + + if length == fileLeecher.pieceSize { + // if the piece is not separated into blocks and is sent in one time + piece.SetData(block) + connInfo.UpdateNbAnswer(true) + err := fileLeecher.pieceComplete(piece, conn) + return err + } + + // else we receive the piece in blocks + blockId := int((begin - int64(fileLeecher.pieceSize)*int64(pieceId))) / CHUNKSIZE + if piece.AddBlockToPiece(blockId, block) { // if we added a block we didn't have + connInfo.UpdateNbAnswer(false) + if piece.IsComplete() { // we have all the blocks of the piece + err := fileLeecher.pieceComplete(piece, conn) + return err + } else { // we need to download the next blocks if possible + piece.downloadNextBlocks() + } + } + fileLeecher.m.Unlock() + return nil +} + +func (fileLeecher *FileLeecher) pieceComplete(piece *Piece, conn ShosetConn) error { + + pieceId := piece.GetId() + + ok := piece.CheckHash() + if !ok { + // download it again + fileLeecher.requestedPieces[pieceId] = false + delete(fileLeecher.piecesBeingRequested, pieceId) + delete(fileLeecher.piecesToRequestPriority, pieceId) // in case if also there + connInfo2 := fileLeecher.pieceAskedToConn[pieceId] + connInfo2.RemovePieceRequested(pieceId) + fileLeecher.m.Unlock() + return fmt.Errorf("piece %d hash is not correct", pieceId) + } + fileLeecher.receivedPieces[pieceId] = true + fileLeecher.requestedPieces[pieceId] = false + delete(fileLeecher.piecesBeingRequested, pieceId) + delete(fileLeecher.piecesToRequestPriority, pieceId) // in case if also there + fileLeecher.nbPiecesReceived++ + conn.GetLogger().Info().Msgf("%v pieceComplete %d from %v total: %d / %d", conn.GetLocalAddress(), piece.GetId(), conn.GetRemoteAddress(), fileLeecher.nbPiecesReceived, fileLeecher.nbPieces) + + // write the piece to the copy file + err := piece.WritePiece() + if err != nil { + fileLeecher.m.Unlock() + return err + } + // update the connection info + connInfo2 := fileLeecher.pieceAskedToConn[pieceId] + connInfo2.RemovePieceRequested(pieceId) + + fileLeecher.SendHaveMessage(pieceId) + // launch the download of the next pieces + if !fileLeecher.isComplete() { + fileLeecher.m.Unlock() + return fileLeecher.DownloadNextPieces(conn) + } else { + // the unlock is in the enddownload function + return fileLeecher.EndDownload() + } +} + +// usefull for the sending to know if we have the piece +func (fileLeecher *FileLeecher) haveChunk(begin int64, size int) bool { + fileLeecher.m.Lock() + defer fileLeecher.m.Unlock() + + pieceId := int(begin / int64(fileLeecher.pieceSize)) + if size <= fileLeecher.pieceSize { + return fileLeecher.receivedPieces[pieceId] + } + return false +} + +// select a new piece to download with a specific algorithm +func (fileLeecher *FileLeecher) selectNewPiece(conn ShosetConn, connInfo ConnInfo) *Piece { + pieceId := -1 + + // we select first the pieces that are in the priority list + for i := range fileLeecher.piecesToRequestPriority { + if !fileLeecher.receivedPieces[i] && connInfo.HavePiece(i) { // if the conn has it + delete(fileLeecher.piecesToRequestPriority, i) + piece, ok := fileLeecher.piecesBeingRequested[i] + if !ok { + break + } + piece.SetConnInfo(connInfo) + return piece + } + } + // we chose the rarest piece among the available pieces + rarestList := []int{} + hightestRarity := -1 + for i, piece := range fileLeecher.receivedPieces { + if !piece && !fileLeecher.requestedPieces[i] && connInfo.HavePiece(i) { // if we don't have it and we haven't requested it elsewhere and the peer has it + newRarity := fileLeecher.pieceRarity[i] + if hightestRarity == -1 || newRarity < hightestRarity { + rarestList = []int{i} + hightestRarity = newRarity + } else if newRarity == hightestRarity { + rarestList = append(rarestList, i) + } + } + } + if len(rarestList) != 0 { + id := rand.Intn(len(rarestList)) + pieceId = rarestList[id] + piece := NewPiece(fileLeecher.SyncFile, pieceId, fileLeecher.pieceSize, fileLeecher.hashMap[pieceId], connInfo) + return piece + } + return nil +} + +// new connection : we can ask him some pieces +func (fileLeecher *FileLeecher) launchDownload(conn ShosetConn) { + connInfo, ok := fileLeecher.ConnInfoMap[conn] + if ok { // if we have already registered the conn + if connInfo.IsReady() { // if we have already received the bitfield from the conn + return + } else { // we ask the bitfield to the conn + go fileLeecher.AskBitfieldFile(conn) + } + } else { // if we have not registered the conn + conn.GetLogger().Info().Msg(conn.GetLocalAddress() + " launchDownload for " + conn.GetRemoteAddress() + " as a new conn") + connInfo = NewConnInfo(conn, fileLeecher, fileLeecher.nbBlocks) + fileLeecher.ConnInfoMap[conn] = connInfo + fileLeecher.FileTransfer.AddFileLeecherToConn(fileLeecher, conn) + go fileLeecher.AskBitfieldFile(conn) + fileLeecher.SendInterested(conn, true) + } +} + +// send to a node that we are interested (or not) in the file +func (fileLeecher *FileLeecher) SendInterested(conn ShosetConn, interested bool) { + message := msg.FileMessage{ + FileUUID: fileLeecher.SyncFile.GetUUID(), + FileName: fileLeecher.File.GetName(), + FileHash: fileLeecher.File.GetHash(), + } + message.InitMessageBase() + if interested { + message.MessageName = "interested" + } else { + message.MessageName = "notInterested" + } + fileLeecher.FileTransfer.SendMessage(message, conn) +} + +// initialize the download of the file with a conn +func (fileLeecher *FileLeecher) InitDownload(conn ShosetConn) { + fileLeecher.m.Lock() + defer fileLeecher.m.Unlock() + fileLeecher.launchDownload(conn) +} + +// we are asked to reduce our number of requests through this conn +func (fileLeecher *FileLeecher) ReduceRequests(conn ShosetConn) { + fileLeecher.m.Lock() + defer fileLeecher.m.Unlock() + conn.GetLogger().Info().Msg("-----------------------ReduceRequests from " + conn.GetRemoteAddress() + " to " + conn.GetLocalAddress()) + connInfo, ok := fileLeecher.ConnInfoMap[conn] + if ok { + connInfo.DecreaseLevel() + } +} + +// we are not authorized to send requests to this conn (we retry after a timeout) +func (fileLeecher *FileLeecher) ReceiveUnauthorisedMessage(conn ShosetConn) { + fileLeecher.m.Lock() + defer fileLeecher.m.Unlock() + fileLeecher.removeConn(conn) +} + +func (fileLeecher *FileLeecher) GetNbRequests(conn ShosetConn) int { + fileLeecher.m.Lock() + defer fileLeecher.m.Unlock() + connInfo, ok := fileLeecher.ConnInfoMap[conn] + if ok { + b, nbMax := connInfo.GetInfoRate() + if b { + return nbMax + 1000 // when asking for entire pieces : count more + } + return nbMax + } + return 0 +} + +// used to ask for more data of the file +func (fileLeecher *FileLeecher) DownloadNextPieces(conn ShosetConn) error { + fileLeecher.m.Lock() + connInfo, ok := fileLeecher.ConnInfoMap[conn] + + canAsk := true + // we download in priority from files where we download block by block + for _, pieceId2 := range connInfo.GetPiecesRequested() { + if priority, ok := fileLeecher.piecesToRequestPriority[pieceId2]; ok && priority { + // we don't want to continue downloading pieces in the priority list + continue + } + piece2, ok := fileLeecher.piecesBeingRequested[pieceId2] + if !ok { + return fmt.Errorf("piece %d is not in the map", pieceId2) + } + if !piece2.GetNoBlock() { + canAsk = piece2.downloadNextBlocks() + } + } + + fileLeecher.m.Unlock() + if !canAsk { // we reached our limit of requests + return nil + } + + if ok && connInfo.IsReady() { + entirePieces, _ := connInfo.GetInfoRate() + if entirePieces { // we ask pieces by pieces + count := 0 // we don't want to double the rate instantly, we go progressively + for connInfo.CanRequestPiece() && count < 2 { + fileLeecher.m.Lock() + piece := fileLeecher.selectNewPiece(conn, connInfo) + if piece != nil { + pieceAdded := connInfo.AddPieceRequested(piece.pieceId) + if pieceAdded { + pieceId := piece.GetId() + fileLeecher.piecesBeingRequested[pieceId] = piece + fileLeecher.requestedPieces[pieceId] = true + fileLeecher.pieceAskedToConn[pieceId] = connInfo + piece.SetNoBlock(true) + piece.downloadPiece() + //fmt.Println(conn.GetLocalAddress(), "is downloading from", conn.GetRemoteAddress(), "piece", piece.GetId(), "with entire pieces : ", entirePieces, "and nb requests : ", nbMax) + } + fileLeecher.m.Unlock() + } else { + fileLeecher.m.Unlock() + //fmt.Println("no piece to download from", conn.GetRemoteAddress()) + return nil + } + count++ + } + + } else if connInfo.GetNbRequestedPieces() <= 1 { // we ask blocks by blocks + // we assume that everytime when we downloadNextBlocks, we fill all the requests possible. If there is a blank, it means we have requested all the blocks of the piece. We can search for another piece. + // this way is smooth : we don't wait for the other piece to be complete. + // we can download up to 2 pieces at the same time + fileLeecher.m.Lock() + piece := fileLeecher.selectNewPiece(conn, connInfo) + if piece != nil { + pieceAdded := connInfo.AddPieceRequested(piece.pieceId) + if pieceAdded { + pieceId := piece.GetId() + fileLeecher.piecesBeingRequested[pieceId] = piece + fileLeecher.pieceAskedToConn[pieceId] = connInfo + fileLeecher.requestedPieces[pieceId] = true + piece.SetNoBlock(false) + //fmt.Println(conn.GetLocalAddress(), "trying to download next blocks from",piece.GetId()) + piece.downloadNextBlocks() + //fmt.Println(conn.GetLocalAddress(), "is downloading from", conn.GetRemoteAddress(), "piece", piece.GetId(), "with entire pieces : ", entirePieces, "and nb requests : ", nbMax) + } + fileLeecher.m.Unlock() + } else { + fileLeecher.m.Unlock() + //fmt.Println(conn.GetLocalAddress(), ": no piece to download from", conn.GetRemoteAddress()) + return nil + } + } + } + return nil +} + +// when the download is complete, we can stop the leecher +func (fileLeecher *FileLeecher) EndDownload() error { + delta := time.Now().UnixMilli() - fileLeecher.startTimestamp + fileLeecher.FileTransfer.GetLogger().Info().Msg("-------------------------file " + fileLeecher.File.GetName() + " is complete ! in " + strconv.FormatInt(delta, 10) + " ms-------------------------") + + if fileLeecher.stopped { + fileLeecher.m.Unlock() + return fmt.Errorf("file leecher already stopped") + } + fileLeecher.stopped = true + fileLeecher.m.Unlock() + + err := fileLeecher.SyncFile.EndDownload() + if err != nil { + hashMap := fileLeecher.SyncFile.GetCopyFile().GetHashMap() + idList := make([]int, 0) + for i := 0; i < len(hashMap); i++ { + if hashMap[i] != fileLeecher.SyncFile.GetCopyFile().GetHash() { + idList = append(idList, i) + } + } + if len(idList) > 0 { + fileLeecher.m.Lock() + for _, id := range idList { + fileLeecher.receivedPieces[id] = false + + } + fileLeecher.stopped = false + fileLeecher.m.Unlock() + for conn := range fileLeecher.ConnInfoMap { + fileLeecher.DownloadNextPieces(conn) + } + } + return err + } + //fileLeecher.FileTransfer.FileSeeders.Store(fileLeecher.SyncFile.GetUUID(), &fileLeecher.FileSeeder) + fileLeecher.FileTransfer.RemoveFileLeecher(fileLeecher) + + message := msg.FileMessage{ + MessageName: "downloadFinished", + FileUUID: fileLeecher.SyncFile.GetUUID(), + FileName: fileLeecher.File.GetName(), + FileHash: fileLeecher.File.GetHash(), + FileVersion: fileLeecher.File.GetVersion(), + } + fileLeecher.FileTransfer.UserPush(&message) + + fileLeecher.FileTransfer.WriteRecords() + return err +} + +func (fileLeecher *FileLeecher) IsComplete() bool { + fileLeecher.m.Lock() + defer fileLeecher.m.Unlock() + return fileLeecher.isComplete() +} +func (fileLeecher *FileLeecher) isComplete() bool { + return fileLeecher.nbPiecesReceived == fileLeecher.nbPieces +} + +func (fileLeecher *FileLeecher) GetBitfield() []bool { + fileLeecher.m.Lock() + defer fileLeecher.m.Unlock() + bitfield := make([]bool, fileLeecher.nbPieces) + for i := 0; i < fileLeecher.nbPieces; i++ { + bitfield[i] = fileLeecher.receivedPieces[i] + } + return bitfield +} + +func (fileLeecher *FileLeecher) GetPieceSize() int { + fileLeecher.m.Lock() + defer fileLeecher.m.Unlock() + return fileLeecher.pieceSize +} + +func (fileLeecher *FileLeecher) GetConnInfo() map[ShosetConn]ConnInfo { + fileLeecher.m.Lock() + defer fileLeecher.m.Unlock() + newMap := make(map[ShosetConn]ConnInfo) + for conn, connInfo := range fileLeecher.ConnInfoMap { + newMap[conn] = connInfo + } + return newMap +} + +// fill the map with the sum of the rate of each conn and the number of rate we add +// it will be used in FileTransfer to monitor the rate of each conn +func (fileLeecher *FileLeecher) UpdateGlobalRate(connMapSum map[ShosetConn]int, connMapCount map[ShosetConn]int) { + fileLeecher.m.Lock() + defer fileLeecher.m.Unlock() + for conn, connInfo := range fileLeecher.ConnInfoMap { + sum, ok := connMapSum[conn] + if !ok { + connMapSum[conn] = connInfo.GetRate() + connMapCount[conn] = 1 + } else { + connMapSum[conn] = sum + connInfo.GetRate() + connMapCount[conn] += 1 + } + } +} + +// ask the bitfield of the file to the conn +// to execute in a goroutine +func (fileLeecher *FileLeecher) AskBitfieldFile(conn ShosetConn) { + count := 0 + for count < 3 { // we try 3 times max + fileLeecher.m.Lock() + _, ok := fileLeecher.GetBitfiledFile[conn] + if ok { // we already have a goroutine trying to ask for the bitfield + fileLeecher.m.Unlock() + return + } + // if we don't hav a goroutine asking for the bitfield, we create one + fileLeecher.GetBitfiledFile[conn] = true + connInfo, ok := fileLeecher.ConnInfoMap[conn] + fileLeecher.m.Unlock() + if ok { + if connInfo.IsReady() { // we received the bitfield + return + } else { + count++ + askBitfield := msg.FileMessage{ + MessageName: "askBitfield", + FileUUID: fileLeecher.SyncFile.GetUUID(), + FileName: fileLeecher.File.GetName(), + FileHash: fileLeecher.File.GetHash(), + PieceSize: fileLeecher.File.GetPieceSize(), + } + askBitfield.InitMessageBase() + fileLeecher.FileTransfer.SendMessage(askBitfield, conn) + time.Sleep(10 * time.Second) + } + } + } +} + +// check the timeout of each pieces we are requesting +// to execute regularly +func (fileLeecher *FileLeecher) CheckRequestTimeout() { + fileLeecher.m.Lock() + defer fileLeecher.m.Unlock() + for pieceId, piece := range fileLeecher.piecesBeingRequested { + if !fileLeecher.piecesToRequestPriority[pieceId] { // if the piece is not a standalone piece + fileLeecher.m.Unlock() + timeoutOccured := piece.CheckRequestTimeout() + fileLeecher.m.Lock() + if timeoutOccured { + connInfo := piece.ConnInformation + if entire, nb := connInfo.GetInfoRate(); !entire && nb == 1 { + // we remove the conn if there is a timeout and the rate is already really slow + fileLeecher.removeConn(connInfo.GetConn()) + } + } + } + } +} + +// when a node has difficulty to give us the piece (too slow or disconnected), we place the piece in a priority list to download. +func (fileLeecher *FileLeecher) AddToRequestPriority(pieceId int, fromConnInfo ConnInfo) { + fileLeecher.m.Lock() + conn := fromConnInfo.GetConn() + fromConnInfo.GetConn().GetLogger().Info().Msgf("%v : adding %d to priority", conn.GetLocalAddress(), pieceId) + fileLeecher.piecesToRequestPriority[pieceId] = true + fromConnInfo.RemovePieceRequested(pieceId) + fileLeecher.m.Unlock() + for c := range fileLeecher.ConnInfoMap { + fileLeecher.DownloadNextPieces(c) + } +} + +func (fileLeecher *FileLeecher) GetHash() string { + fileLeecher.m.Lock() + defer fileLeecher.m.Unlock() + return fileLeecher.hash +} diff --git a/file/fileLibrary.go b/file/fileLibrary.go new file mode 100644 index 0000000..bd5ae92 --- /dev/null +++ b/file/fileLibrary.go @@ -0,0 +1,378 @@ +package fileSync + +import ( + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + "sync" + + "github.com/ditrit/shoset/msg" +) + +/* +This file is used to describe a libray linked to a shoset +It contains some tools like manipulating files and directories and loading the library +*/ + +type FileLibrary interface { + // Lock the library mutex. A real file is added by the user. We create the copy file and add the syncFile to the library + UploadFile(file File) (SyncFile, error) + // create a syncFile from the copy file and the uuid + // used when we receive a new file from another node + CreateFile(file File, uuid string) (SyncFile, error) + GetFile(uuid string) (SyncFile, error) + GetDir() string + // Update all the files in the library and create leechers if necessary + // it compare the list of file state given with our library + UpdateLibrary(listFiles []FileState, conn ShosetConn) + // Print info of every files in the library + PrintAllFiles() + // get the some basic info about our library in a message format + GetMessageLibrary() (*msg.FileMessage, error) + // Add all the files that are sync to the library + LoadLibrary() error + SetFileTransfer(fileTransfer FileTransfer) + // not used yet + // we want to keep a trace of a file that has been deleted + DeleteFile(uuid string) + GetHash() string + // move a file/folder to another place inside the library + Move(from string, to string) error + // add a file to the library + // the file has already been copied in the library, we just have to add it to the library and to synchronise it + Add(libraryPath string) error + // remove a file from the library + Remove(path string) error + // a file has been modified in the library path + Modify(libraryPath string) error +} + +type FileLibraryImpl struct { + FilesMap map[string]SyncFile // Links a uuid to a pointer to a File object -> []*File + PathUUIDMap map[string]string // Links a path (realPath) to a uuid + libraryDir string //path to the library directory : directory where all the files are stored + hash string + FileTransfer FileTransfer + m sync.Mutex +} + +// Create new empty FileLibrary +func NewFileLibrary(libraryDir string) (*FileLibraryImpl, error) { + fileLibrary := FileLibraryImpl{} + fileLibrary.FilesMap = make(map[string]SyncFile) + fileLibrary.PathUUIDMap = make(map[string]string) + fileLibrary.libraryDir = libraryDir + + err := fileLibrary.LoadLibrary() + + return &fileLibrary, err +} + +// Add a new file to the FileLibrary +func (fileLibrary *FileLibraryImpl) addNewSyncFile(syncFile SyncFile) { + fileLibrary.FilesMap[syncFile.GetUUID()] = syncFile + fileLibrary.PathUUIDMap[syncFile.GetRealFile().GetRelativePath()+syncFile.GetRealFile().GetName()] = syncFile.GetUUID() + fmt.Println("New file added to the library : ", syncFile) + fileLibrary.calculateHash() +} + +// Lock the library mutex. A real file is added by the user. We create the copy file and add the syncFile to the library +func (fileLibrary *FileLibraryImpl) UploadFile(file File) (SyncFile, error) { + fileLibrary.m.Lock() + defer fileLibrary.m.Unlock() + return fileLibrary.uploadFile(file) +} + +// a real file is added by the user. We create the copy file and add the syncFile to the library +func (fileLibrary *FileLibraryImpl) uploadFile(file File) (SyncFile, error) { + syncFile := NewSyncFile(fileLibrary.libraryDir) + syncFile.SetFileTransfer(fileLibrary.FileTransfer) + syncFile.RealFile = file + var err error + syncFile.CopyFile, err = LoadFile(fileLibrary.libraryDir, RealToCopyPath(file.GetRelativePath()), file.GetName()) + if err != nil { + return nil, err + } + syncFile.WriteRealToCopy() + if err != nil { + return nil, err + } + fileLibrary.addNewSyncFile(syncFile) + return syncFile, nil +} + +// create a syncFile from the copy file and the uuid +// used when we receive a new file from another node +func (fileLibrary *FileLibraryImpl) CreateFile(file File, uuid string) (SyncFile, error) { + fileLibrary.m.Lock() + defer fileLibrary.m.Unlock() + syncFile := NewSyncFile(fileLibrary.libraryDir) + syncFile.SetFileTransfer(fileLibrary.FileTransfer) + syncFile.SetUUID(uuid) + syncFile.CopyFile = file + var err error + syncFile.RealFile, err = LoadFile(fileLibrary.libraryDir, CopyToRealPath(file.GetRelativePath()), file.GetName()) + if err != nil { + return nil, err + } + fileLibrary.addNewSyncFile(syncFile) + return syncFile, nil +} + +func (fileLibrary *FileLibraryImpl) GetFile(uuid string) (SyncFile, error) { + fileLibrary.m.Lock() + defer fileLibrary.m.Unlock() + file, ok := fileLibrary.FilesMap[uuid] + var err error + if !ok { + err = fmt.Errorf("File %s not found in library", uuid) + } + return file, err +} +func (fileLibrary *FileLibraryImpl) GetDir() string { + fileLibrary.m.Lock() + defer fileLibrary.m.Unlock() + return fileLibrary.libraryDir +} + +// Update all the files in the library and create leechers if necessary +// it compare the list of file state given with our library +func (fileLibrary *FileLibraryImpl) UpdateLibrary(listFiles []FileState, conn ShosetConn) { + for _, fileState := range listFiles { + syncFile, err := fileLibrary.GetFile(fileState.UUID) + if err != nil { + go fileLibrary.FileTransfer.AskInfoFile(conn, fileState) + } else { + err := syncFile.UpdateFile(fileState, conn) + if err != nil { // conflict + // TODO + fmt.Println(err) + } + } + + } +} + +// Print info of every files in the library +func (fileLibrary *FileLibraryImpl) PrintAllFiles() { + fileLibrary.m.Lock() + defer fileLibrary.m.Unlock() + result := "\nList of imported files in fileLibrary :" + for name, file := range fileLibrary.FilesMap { + result += "\nName (library) : " + name + "\n" + result += file.String() + } + + fmt.Println(result) +} + +// get the some basic info about our library in a message format +func (fileLibrary *FileLibraryImpl) GetMessageLibrary() (*msg.FileMessage, error) { + fileLibrary.m.Lock() + defer fileLibrary.m.Unlock() + listFileState := make([]msg.FileStateMessage, 0) + for _, syncFile := range fileLibrary.FilesMap { + listFileState = append(listFileState, syncFile.GetFileState().FileStateMessage()) + } + info := msg.FileMessage{MessageName: "sendLibrary", + Library: listFileState, + } + info.InitMessageBase() + return &info, nil +} + +// Add all the files that are sync to the library +func (fileLibrary *FileLibraryImpl) LoadLibrary() error { + fileLibrary.m.Lock() + defer fileLibrary.m.Unlock() + path, err := os.Open(filepath.Join(fileLibrary.libraryDir, PATH_COPY_FILES, ".info")) + path.Close() + if os.IsNotExist(err) { // if the repertory doesn't exist + // we create it + err = os.MkdirAll(filepath.Join(fileLibrary.libraryDir, PATH_COPY_FILES, ".info"), 0755) + return err + } else if err != nil { + return err + } + path, err = os.Open(filepath.Join(fileLibrary.libraryDir, PATH_COPY_FILES, ".info")) + if err != nil { + return err + } + files, err := path.ReadDir(0) + if err != nil { + return err + } + + list_files := []string{} + for _, v := range files { + list_files = append(list_files, v.Name()) + } + path.Close() + + for _, fileName := range list_files { + syncFile, err := LoadSyncFileInfo(fileLibrary.libraryDir, filepath.Join(fileLibrary.libraryDir, PATH_COPY_FILES, ".info", fileName)) + if err != nil { + return err + } + syncFile.SetFileTransfer(fileLibrary.FileTransfer) + fileLibrary.addNewSyncFile(syncFile) + } + return nil +} + +func (fileLibrary *FileLibraryImpl) SetFileTransfer(fileTransfer FileTransfer) { + fileLibrary.FileTransfer = fileTransfer +} + +// not used yet +// we want to keep a trace of a file that has been deleted +func (fileLibrary *FileLibraryImpl) DeleteFile(uuid string) { + fileLibrary.m.Lock() + defer fileLibrary.m.Unlock() + delete(fileLibrary.FilesMap, uuid) +} + +// calculate the hash of the library (with the hash, name, relative path and version of every file) +func (fileLibrary *FileLibraryImpl) calculateHash() { + toHash := "" + for _, file := range fileLibrary.FilesMap { + toHash += file.GetCopyFile().GetHash() + toHash += file.GetCopyFile().GetName() + toHash += file.GetCopyFile().GetRelativePath() + toHash += strconv.Itoa(file.GetCopyFile().GetVersion()) + } + fileLibrary.hash = Hash([]byte(toHash)) +} + +func (fileLibrary *FileLibraryImpl) GetHash() string { + fileLibrary.m.Lock() + defer fileLibrary.m.Unlock() + return fileLibrary.hash +} + +// move a file/folder to another place inside the library +func (fileLibrary *FileLibraryImpl) Move(from string, to string) error { + fileLibrary.m.Lock() + defer fileLibrary.m.Unlock() + op := Operation{Name: "modify", File: from, NewFile: to} + fromFile, err := os.Stat(filepath.Join(fileLibrary.libraryDir, from)) + if err != nil { + return fmt.Errorf("can't get the file : %s", err) + } + if !fromFile.IsDir() { // if we are moving a file + relpath := filepath.Dir(from) + uuid, ok := fileLibrary.PathUUIDMap[relpath] + if ok { // if we have the file in the library + syncFile, ok := fileLibrary.FilesMap[uuid] + if ok { + err := syncFile.ApplyOperationFromMe(op) + if err != nil { + return err + } + } else { + delete(fileLibrary.PathUUIDMap, relpath) + } + } else { // if we don't have the file in the library + return fmt.Errorf("the file is not in the library") + } + } else { // if it's a directory + // we move all the files inside + for _, syncFile := range fileLibrary.FilesMap { + if strings.HasPrefix(syncFile.GetRealFile().GetRelativePath(), from) { + err := syncFile.ApplyOperationFromMe(op) + if err != nil { + return err + } + } + } + } + return nil +} + +// add a file to the library +// the file has already been copied in the library, we just have to add it to the library and to synchronise it +func (fileLibrary *FileLibraryImpl) Add(libraryPath string) error { + fileLibrary.m.Lock() + defer fileLibrary.m.Unlock() + // we upload it + relPath := PathToRelativePath(libraryPath, fileLibrary.libraryDir) + relativePath := Dir(relPath) + fileName := filepath.Base(libraryPath) + + _, ok := fileLibrary.PathUUIDMap[relPath] + if ok { + return fmt.Errorf("the file is already in the library : %s", relativePath) + } + + file, err := LoadFile(fileLibrary.libraryDir, relativePath, fileName) + if err != nil { + return fmt.Errorf("can't add the file : %s", err) + } + syncFile, err := fileLibrary.uploadFile(file) + if err != nil { + return fmt.Errorf("can't upload the file : %s", err) + } + err = syncFile.ApplyOperationFromMe(Operation{Name: "create"}) + return err +} + +// remove a file from the library +func (fileLibrary *FileLibraryImpl) Remove(path string) error { + fileLibrary.m.Lock() + defer fileLibrary.m.Unlock() + relPath := PathToRelativePath(path, fileLibrary.libraryDir) + uuid, ok := fileLibrary.PathUUIDMap[relPath] + if ok { // if we have the file in the library + file, ok := fileLibrary.FilesMap[uuid] + if ok { // if we don't have the file in the library + err := file.ApplyOperationFromMe(Operation{Name: "remove"}) + if err != nil { + return err + } + } else { + delete(fileLibrary.PathUUIDMap, relPath) + } + } else { // if we don't have the file in the library + // maybe it was a directory + for _, syncFile := range fileLibrary.FilesMap { + if strings.HasPrefix(syncFile.GetRealFile().GetRelativePath(), relPath) { + err := syncFile.ApplyOperationFromMe(Operation{Name: "remove"}) + if err != nil { + return err + } + } + } + err := os.RemoveAll(path) + if err != nil { + return fmt.Errorf("can't remove the file : %s", err) + } + } + return nil +} + +// a file has been modified in the library path +func (fileLibrary *FileLibraryImpl) Modify(libraryPath string) error { + fileLibrary.m.Lock() + defer fileLibrary.m.Unlock() + relPath := PathToRelativePath(libraryPath, fileLibrary.libraryDir) + uuid, ok := fileLibrary.PathUUIDMap[relPath] + if ok { // if we have the file in the library + syncFile, ok := fileLibrary.FilesMap[uuid] + if ok { + if syncFile.GetStatus() == "downloading" { + return fmt.Errorf("the file %s is currently downloading", libraryPath) + } + err := syncFile.ApplyOperationFromMe(Operation{Name: "modify"}) + if err != nil { + return fmt.Errorf("can't modify the file : %s", err) + } + } else { + delete(fileLibrary.PathUUIDMap, relPath) + } + } else { // we don't have this file in the library + return fmt.Errorf("the file is not in the library") + } + return nil +} diff --git a/file/fileMessageQueue.go b/file/fileMessageQueue.go new file mode 100644 index 0000000..18a56cf --- /dev/null +++ b/file/fileMessageQueue.go @@ -0,0 +1,62 @@ +package fileSync + +import ( + "container/list" + "sync" + + "github.com/ditrit/shoset/msg" +) + +/* +It implements a queue with a channel and lock functionnality. +It is used if FileTransfer to handle messages to send and to receive. +*/ + +type MessageQueue struct { + qlist list.List + newMessage chan bool + m sync.Mutex +} + +// NewMessageQueue : constructor +func NewMessageQueue() *MessageQueue { + q := new(MessageQueue) + q.qlist.Init() + q.newMessage = make(chan bool, 1000) + return q +} + +func (q *MessageQueue) Push(m *msg.FileMessage) { + q.m.Lock() + q.qlist.PushBack(m) + q.m.Unlock() + q.newMessage <- true +} + +func (q *MessageQueue) Pop() *msg.FileMessage { + q.m.Lock() + defer q.m.Unlock() + e := q.qlist.Front() + if e != nil { + q.qlist.Remove(e) + return e.Value.(*msg.FileMessage) + } + return nil +} + +func (q *MessageQueue) GetChan() chan bool { + return q.newMessage +} + +// used by the piece structure for timeout to avoid reasking file we received +func (q *MessageQueue) HasChunk(begin int64) bool { + q.m.Lock() + defer q.m.Unlock() + for e := q.qlist.Front(); e != nil; e = e.Next() { + message := e.Value.(*msg.FileMessage) + if message.Begin == begin { + return true + } + } + return false +} diff --git a/file/fileSeeder.go b/file/fileSeeder.go new file mode 100644 index 0000000..5810539 --- /dev/null +++ b/file/fileSeeder.go @@ -0,0 +1,81 @@ +package fileSync + +import ( + "fmt" + "sync" + "time" + + "github.com/ditrit/shoset/msg" +) + +/* +This file is used to describe a file seeder +It is used when we have the full file and we want to share it +This small structure load the data from the wanted file and send it to the requester +*/ + +type FileSeeder struct { + SyncFile SyncFile + File File + FileTransfer FileTransfer + DataAskedPerConn sync.Map //map[ShosetConn][int64]int // list of conn and the data they asked : to not send them twice the same data + InterestedConn sync.Map //map[ShosetConn]bool // list of interested conn + m sync.Mutex + lastSent int64 // timestamp of last sent block : used to delete the seeder if no block is sent for a long time +} + +func (fileSeeder *FileSeeder) InitSeeder(syncFile SyncFile, fileTransfer FileTransfer) { + fileSeeder.SyncFile = syncFile + fileSeeder.File = syncFile.GetCopyFile() + fileSeeder.FileTransfer = fileTransfer + fileSeeder.InterestedConn = sync.Map{} + fileSeeder.lastSent = time.Now().UnixMilli() +} + +// someone request a block of a piece +func (fileSeeder *FileSeeder) sendBlock(conn ShosetConn, begin int64, length int) error { + // check if we didn't already send the block to the conn + fileSeeder.m.Lock() + defer fileSeeder.m.Unlock() + beginsMap := make(map[int64]int) + begins, ok := fileSeeder.DataAskedPerConn.Load(conn) + if ok { + beginsMap = begins.(map[int64]int) + length2, ok := beginsMap[begin] + if ok { // he already asked with this begin + if length2 == length { // he already asked with this begin and this length + return fmt.Errorf("already sent") + } + } + } + beginsMap[begin] = length + fileSeeder.DataAskedPerConn.Store(conn, beginsMap) + fileSeeder.lastSent = time.Now().UnixMilli() + + // load the data + data, err := fileSeeder.File.LoadData(begin, length) + if err != nil { + return err + } + + // send the block + + responseMsg := msg.FileMessage{ + MessageName: "sendChunk", + FileUUID: fileSeeder.SyncFile.GetUUID(), + FileName: fileSeeder.File.GetName(), + FileHash: fileSeeder.File.GetHash(), + Begin: begin, + Length: length, + ChunkData: data, + } + responseMsg.InitMessageBase() + fileSeeder.FileTransfer.SendMessage(responseMsg, conn) + return err +} + +func (fileSeeder *FileSeeder) GetLastSent() int64 { + fileSeeder.m.Lock() + defer fileSeeder.m.Unlock() + return fileSeeder.lastSent +} diff --git a/file/fileTransfer.go b/file/fileTransfer.go new file mode 100644 index 0000000..34e5e6e --- /dev/null +++ b/file/fileTransfer.go @@ -0,0 +1,912 @@ +package fileSync + +import ( + "errors" + "fmt" + "log" + "math/rand" + "path/filepath" + "sort" + "sync" + "time" + + "github.com/ditrit/shoset/msg" + "github.com/rs/zerolog" +) + +/* +This file handle messages for file transfer. +It monitor the flow rate of the network and send congestion messages to the network. + +*/ + +type Rate struct { + minTimeConn map[ShosetConn]int // min RTT for each conn + + m sync.RWMutex +} + +func NewRate() *Rate { + var Rate Rate + Rate.minTimeConn = make(map[ShosetConn]int) + return &Rate +} + +func (rate *Rate) GetMin(conn ShosetConn) int { + rate.m.Lock() + defer rate.m.Unlock() + min, ok := rate.minTimeConn[conn] + if !ok { + return 10000 + } + return min +} + +func (rate *Rate) GetNbConn() int { + rate.m.Lock() + defer rate.m.Unlock() + return len(rate.minTimeConn) +} + +func (rate *Rate) SetMin(conn ShosetConn, min int) { + rate.m.Lock() + defer rate.m.Unlock() + rate.minTimeConn[conn] = min +} + +type FileTransfer interface { + Init(library FileLibrary, logger zerolog.Logger, userMessageQueue *msg.Queue, broadcast func(message *msg.FileMessage)) + RemoveFileSeeder(fileSeeder *FileSeeder) + AddFileLeecher(fileLeecher *FileLeecher) + AddFileLeecherToConn(fileLeecher *FileLeecher, conn ShosetConn) + RemoveFileLeecher(fileLeecher *FileLeecher) + ReceiveCongestionMessage(conn ShosetConn) + SendCongestionMessage(conn ShosetConn) + ReceiveUnauthorisedMessage(conn ShosetConn) + SendUnauthorisedMessage(conn ShosetConn) + // receive a message "authorised" from a conn + ReceiveAuthorisedMessage(conn ShosetConn) + // send a the message "authorised" to a conn + SendAuthorisedMessage(conn ShosetConn) + ReceiveHaveMessage(conn ShosetConn, message *msg.FileMessage) + ReceiveBitfieldMessage(conn ShosetConn, message *msg.FileMessage) + // if someone is asking us our bitfield of a file + ReceiveAskBitfieldMessage(conn ShosetConn, message *msg.FileMessage) + ReceiveInterestedMessage(conn ShosetConn, message *msg.FileMessage) + IsAuthorised(conn ShosetConn) bool + // a new conn is sending us requests + AddUploadConn(conn ShosetConn) + // remove a conn from the list of conn that are downloading from us + RemoveConn(conn ShosetConn) + AskChunk(conn ShosetConn, message *msg.FileMessage, sendRate bool) + ReceiveChunk(conn ShosetConn, message *msg.FileMessage) error + ReceiveAskChunk(conn ShosetConn, message *msg.FileMessage) error + ReceiveAskInfoMessage(conn ShosetConn, message *msg.FileMessage) + GetMissingLength() int64 + DecreaseMissingLength(length int) + // keep asking info if we don't have this file + // to launch in a goroutine + AskInfoFile(conn ShosetConn, fileState FileState) + InitLeecher(syncFile SyncFile, conn ShosetConn) + ReceiveMessage(message *msg.FileMessage, conn ShosetConn) + HandleReceiveMessage(messageQueue *MessageQueue, conn ShosetConn) + HandleReceiveMessageFromQueue(fileMessage msg.FileMessage, c ShosetConn) error + SendMessage(message msg.FileMessage, conn ShosetConn) + HandleSendMessage(messageQueue *MessageQueue, conn ShosetConn) + HandleSendMessageFromQueue(fileMessage msg.FileMessage, c ShosetConn) error + SetExternalCommands(ec *ExternalCommands) + SetNbConn(nbConn int) + GetLibrary() FileLibrary + Broadcast(message *msg.FileMessage) + UserPush(message *msg.FileMessage) + DeleteLeecher(fileUUID string) + GetLeecher(fileUUID string) *FileLeecher + WriteRecords() + GetLogger() *zerolog.Logger + GetReceiveQueue(conn *ShosetConn) *MessageQueue +} + +// global struct to handle multiple file transfer +type FileTransferImpl struct { + // attributes with no mutex to be accessible quickly + Library FileLibrary // file library + FileSeeders sync.Map //map[string]*FileSeeder // map of fileSeeder + FileLeechers sync.Map //map[string]*FileLeecher // map of fileLeecher + FileLeechersPerConn sync.Map //map[ShosetConn][]*FileLeecher // map of fileLeecher per conn + AuthorisedConn sync.Map //map[ShosetConn]bool // list of authorised conn : conn that can send us requests + ConnLastTime sync.Map //map[ShosetConn]int64 // last time a conn sent a request + MissingDataConn sync.Map //map[ShosetConn]int64 // map of missing data (in bytes) per conn + DecreaseConn sync.Map //map[ShosetConn]int64 // map of conn with a timestamp to know the last time we send a congestion message + RTTRate *Rate + + missingLength int64 // length of our missing data + GetInfoMap map[string]string // map of {uuid : hash} hash being asked + stop chan bool + Logger zerolog.Logger + SendQueue sync.Map // map[*ShosetConn]MessageQueue + ReceiveQueue sync.Map // map[*ShosetConn]MessageQueue + LogRecords LogRateTransfer + broadcast func(message *msg.FileMessage) + userMessageQueue *msg.Queue + nbConn int + + ExternalCommands *ExternalCommands + m sync.Mutex +} + +func (ft *FileTransferImpl) Init(library FileLibrary, logger zerolog.Logger, userMessageQueue *msg.Queue, broadcast func(message *msg.FileMessage)) { + ft.FileSeeders = sync.Map{} + ft.FileLeechers = sync.Map{} + ft.FileLeechersPerConn = sync.Map{} + ft.AuthorisedConn = sync.Map{} + ft.ConnLastTime = sync.Map{} + ft.MissingDataConn = sync.Map{} + ft.DecreaseConn = sync.Map{} + ft.RTTRate = NewRate() + ft.broadcast = broadcast + + ft.GetInfoMap = make(map[string]string) + ft.stop = make(chan bool) + ft.Logger = logger + ft.userMessageQueue = userMessageQueue + ft.SendQueue = sync.Map{} + ft.ReceiveQueue = sync.Map{} + + ft.watchRequestsTimeout(1000) + ft.watchRTT(500) + + ft.Library = library + library.SetFileTransfer(ft) + ft.LogRecords = *NewLogRateTransfer(filepath.Join(ft.Library.GetDir(), PATH_COPY_FILES, ".info", "flowrate.csv")) + + ft.LogRecords.createMonitorFile() +} + +func (ft *FileTransferImpl) RemoveFileSeeder(fileSeeder *FileSeeder) { + ft.FileSeeders.Delete(fileSeeder.SyncFile.GetUUID()) +} + +func (ft *FileTransferImpl) AddFileLeecher(fileLeecher *FileLeecher) { + ft.FileLeechers.Store(fileLeecher.SyncFile.GetUUID(), fileLeecher) + ft.m.Lock() + delete(ft.GetInfoMap, fileLeecher.SyncFile.GetUUID()) + ft.missingLength += fileLeecher.File.GetSize() + ft.m.Unlock() +} + +func (ft *FileTransferImpl) AddFileLeecherToConn(fileLeecher *FileLeecher, conn ShosetConn) { + leechers, ok := ft.FileLeechersPerConn.Load(conn) + if ok { + ft.FileLeechersPerConn.Store(conn, append(leechers.([]*FileLeecher), fileLeecher)) + } else { + ft.FileLeechersPerConn.Store(conn, []*FileLeecher{fileLeecher}) + } +} + +func (ft *FileTransferImpl) RemoveFileLeecher(fileLeecher *FileLeecher) { + ft.m.Lock() + defer ft.m.Unlock() + uuid := fileLeecher.SyncFile.GetUUID() + ft.FileLeechers.Delete(uuid) + connInfo := fileLeecher.GetConnInfo() + for conn := range connInfo { + leechersList, ok := ft.FileLeechersPerConn.Load(conn) + if ok { + leechers := leechersList.([]*FileLeecher) + for i, leecher := range leechers { + if leecher.SyncFile.GetUUID() == uuid { + leechers = append(leechers[:i], leechers[i+1:]...) + break + } + } + if len(leechers) == 0 { + ft.FileLeechersPerConn.Delete(conn) + fileLeecher.SendInterested(conn, false) + } else { + ft.FileLeechersPerConn.Store(conn, leechers) + } + } + } +} + +func (ft *FileTransferImpl) ReceiveCongestionMessage(conn ShosetConn) { + ft.reduceRequests(conn) +} + +func (ft *FileTransferImpl) SendCongestionMessage(conn ShosetConn) { + message := msg.FileMessage{ + MessageName: "congestion", + } + message.InitMessageBase() + ft.SendMessage(message, conn) +} + +func (ft *FileTransferImpl) ReceiveUnauthorisedMessage(conn ShosetConn) { + leechers, ok := ft.FileLeechersPerConn.Load(conn) + if ok { + for _, leecher := range leechers.([]*FileLeecher) { + leecher.ReceiveUnauthorisedMessage(conn) + } + } +} + +func (ft *FileTransferImpl) SendUnauthorisedMessage(conn ShosetConn) { + message := msg.FileMessage{ + MessageName: "unauthorised", + } + message.InitMessageBase() + ft.SendMessage(message, conn) + conn.GetLogger().Info().Msg("------------------------------" + conn.GetRemoteAddress() + "is unauthorised -------------------------------") +} + +// receive a message "authorised" from a conn +func (ft *FileTransferImpl) ReceiveAuthorisedMessage(conn ShosetConn) { + ft.AuthorisedConn.Store(conn, true) +} + +// send a the message "authorised" to a conn +func (ft *FileTransferImpl) SendAuthorisedMessage(conn ShosetConn) { + message := msg.FileMessage{ + MessageName: "authorised", + } + message.InitMessageBase() + ft.SendMessage(message, conn) +} + +func (ft *FileTransferImpl) ReceiveHaveMessage(conn ShosetConn, message *msg.FileMessage) { + leecher, ok := ft.FileLeechers.Load(message.FileUUID) + if ok { + leecher.(*FileLeecher).ReceiveHaveMessage(conn, message.PieceNumber) + } +} + +func (ft *FileTransferImpl) ReceiveBitfieldMessage(conn ShosetConn, message *msg.FileMessage) { + leecher, ok := ft.FileLeechers.Load(message.FileUUID) + if ok { + leecher.(*FileLeecher).ReceiveBitfieldMessage(conn, message.Bitfield) + } else { + log.Println("ReceiveBitfieldMessage: leecher not found for uuid :", message.FileUUID) + } +} + +// if someone is asking us our bitfield of a file +func (ft *FileTransferImpl) ReceiveAskBitfieldMessage(conn ShosetConn, message *msg.FileMessage) { + answer := msg.FileMessage{ + MessageName: "sendBitfield", + FileUUID: message.FileUUID, + FileName: message.FileName, + FileHash: message.FileHash, + PieceSize: message.PieceSize, + } + answer.InitMessageBase() + syncFile, err := ft.Library.GetFile(message.FileUUID) + if err != nil { // we don't have this file and we are not downloading it + // we try to have the leecher if we are downloading it. + ft.SendMessage(answer, conn) + } else { // we have this file or we are downloading it + if syncFile.GetCopyFile().GetHash() != message.FileHash { // we have this file but it is not the same + // we send short info about the file + ft.SendMessage(answer, conn) + shortInfo := syncFile.GetShortInfoMsg() + ft.SendMessage(shortInfo, conn) + } else { // we have this file and it is the same + leecher, ok := ft.FileLeechers.Load(message.FileUUID) + if ok { // we have a leecher for this file + leecher := leecher.(*FileLeecher) + bitfield := leecher.GetBitfield() + answer.Bitfield = bitfield + ft.SendMessage(answer, conn) + // we add this connection too because he is probably going to download the file too + leecher.InitDownload(conn) + } else { // we don't have a leecher for this file + nbPieces := int(syncFile.GetCopyFile().GetSize() / int64(message.PieceSize)) + bitfield := make([]bool, nbPieces) + for i := 0; i < nbPieces; i++ { + bitfield[i] = true + } + answer.Bitfield = bitfield + ft.SendMessage(answer, conn) + } + } + } +} + +func (ft *FileTransferImpl) ReceiveInterestedMessage(conn ShosetConn, message *msg.FileMessage) { + fileUUID := message.FileUUID + seeder, ok := ft.FileSeeders.Load(fileUUID) + if ok { + if message.MessageName == "interested" { + conn.GetLogger().Info().Msg("-------------" + conn.GetRemoteAddress() + "is interested in" + conn.GetLocalAddress()) + seeder.(*FileSeeder).InterestedConn.Store(conn, true) + } else { + seeder.(*FileSeeder).InterestedConn.Delete(conn) + conn.GetLogger().Info().Msg("-------------" + conn.GetRemoteAddress() + "is really not interested in" + conn.GetLocalAddress()) + + count := 0 + seeder.(*FileSeeder).InterestedConn.Range(func(key, value interface{}) bool { + count++ + return true + }) + if count == 0 { + ft.LogRecords.writeRecords() + } + } + return + } + leecher, ok := ft.FileLeechers.Load(fileUUID) + if ok { + if message.MessageName == "interested" { + leecher.(*FileLeecher).InterestedConn.Store(conn, true) + conn.GetLogger().Info().Msg("-------------" + conn.GetRemoteAddress() + "is interested in" + conn.GetLocalAddress()) + } else { + leecher.(*FileLeecher).InterestedConn.Delete(conn) + conn.GetLogger().Info().Msg("-------------" + conn.GetRemoteAddress() + "is really not interested in" + conn.GetLocalAddress()) + + count := 0 + leecher.(*FileLeecher).InterestedConn.Range(func(key, value interface{}) bool { + count++ + return true + }) + if count == 0 { + ft.LogRecords.writeRecords() + } + } + return + } +} + +func (ft *FileTransferImpl) IsAuthorised(conn ShosetConn) bool { + _, ok := ft.AuthorisedConn.Load(conn) + return ok +} + +// a new conn is sending us requests +func (ft *FileTransferImpl) AddUploadConn(conn ShosetConn) { + ft.AuthorisedConn.Store(conn, true) + ft.ConnLastTime.Store(conn, time.Now().Unix()) +} + +// remove a conn from the list of conn that are downloading from us +func (ft *FileTransferImpl) RemoveConn(conn ShosetConn) { + ft.AuthorisedConn.Delete(conn) + ft.ConnLastTime.Delete(conn) +} + +func (ft *FileTransferImpl) monitorDecrease(decreasingConn []ShosetConn) { + mapCongestionConn := make(map[ShosetConn]bool) + mapPiecesConn := make(map[ShosetConn]int64) + + // get the ordered list of conn (ascending order) + ft.MissingDataConn.Range(func(key, value interface{}) bool { + mapPiecesConn[key.(ShosetConn)] = value.(int64) + return true + }) + connList := make([]ShosetConn, 0, ft.RTTRate.GetNbConn()) // list of all the conn we are uploading to + ft.AuthorisedConn.Range(func(key, value interface{}) bool { + connList = append(connList, key.(ShosetConn)) + return true + }) + sort.Slice(connList, func(i, j int) bool { return mapPiecesConn[connList[i]] < mapPiecesConn[connList[j]] }) + nbConn := len(connList) + mapPlaceConn := make(map[ShosetConn]int) + for i, conn := range connList { + mapPlaceConn[conn] = i * 100 / nbConn + } + for _, conn := range decreasingConn { + // we don't take care of the conn that have decreased for less than 2s + isDecreasing, ok := ft.DecreaseConn.Load(conn) + if ok { + if time.Now().UnixMilli()-isDecreasing.(int64) < 4000 { // if the conn is decreasing for less than ...s + //fmt.Println("conn", conn.GetRemoteAddress(), "is decreasing for less than 4s") + continue + } else { + ft.DecreaseConn.Delete(conn) + } + } + for i, c := range connList { + rd := rand.Intn(100) + 1 + if i*100/nbConn*mapPlaceConn[conn]/100 > rd { // with random, higher chance to be picked if have a lot of pieces and the decreasing conn have not a lot of pieces + mapCongestionConn[c] = true + } + } + } + for conn, b := range mapCongestionConn { + if b { + ft.DecreaseConn.Store(conn, time.Now().UnixMilli()) + ft.SendCongestionMessage(conn) + conn.GetLogger().Info().Msg("-----------------------sending ReduceRequests from " + conn.GetLocalAddress() + " to " + conn.GetRemoteAddress()) + } + + } + for _, conn := range decreasingConn { + ft.reduceRequests(conn) + } +} + +func (ft *FileTransferImpl) reduceRequests(conn ShosetConn) { + listFileLeecher, ok := ft.FileLeechersPerConn.Load(conn) + if !ok { + return + } + worseLeecher := listFileLeecher.([]*FileLeecher)[0] + worseLeecherScore := 0 + for _, fileLeecher := range listFileLeecher.([]*FileLeecher) { + score := fileLeecher.GetNbRequests(conn) + if score > worseLeecherScore { // reduce the number of requests for the file that does the most requests + worseLeecher = fileLeecher + worseLeecherScore = score + } + } + worseLeecher.ReduceRequests(conn) +} + +func (ft *FileTransferImpl) updateRTT() { + toDecrease := []ShosetConn{} + ft.AuthorisedConn.Range(func(key, value interface{}) bool { + conn := key.(ShosetConn) + min := ft.RTTRate.GetMin(conn) + tcp, err := conn.GetTCPInfo() + if err != nil { + fmt.Println(err) + } + rtt := int(tcp.Rtt) / 1000 + if rtt < min { + ft.RTTRate.SetMin(conn, Max(rtt, 10)) + } else if rtt > min*8 { + //fmt.Println(conn.GetLocalAddress(), "diminish", conn.GetRemoteAddress(), "because rtt is", rtt, "and min is", min) + toDecrease = append(toDecrease, conn) + } + return true + }) + if len(toDecrease) > 0 { + ft.monitorDecrease(toDecrease) + } +} + +func (ft *FileTransferImpl) watchRTT(nbMillisec int) { + sleepDuration := time.Duration(nbMillisec) * time.Millisecond + ticker := time.NewTicker(sleepDuration) + + go func() { + for { + select { + case <-ticker.C: + ft.updateRTT() + case <-ft.stop: + ticker.Stop() + return + } + } + }() +} + +func (ft *FileTransferImpl) AskChunk(conn ShosetConn, message *msg.FileMessage, sendRate bool) { + rate := 0 + message.Rate = rate + ft.SendMessage(*message, conn) +} + +func (ft *FileTransferImpl) ReceiveChunk(conn ShosetConn, message *msg.FileMessage) error { + fileLeecher, ok := ft.FileLeechers.Load(message.FileUUID) + if !ok { + return fmt.Errorf("can't find leecher for file %s", message.FileName) + } + err := fileLeecher.(*FileLeecher).ReceiveChunk(conn, message.Begin, message.Length, message.ChunkData) + return err +} + +func (ft *FileTransferImpl) ReceiveAskChunk(conn ShosetConn, message *msg.FileMessage) error { + ft.MissingDataConn.Store(conn, message.MissingLength) + if ft.IsAuthorised(conn) { + origin, ok := ft.FileSeeders.Load(message.FileUUID) + if !ok { // there is no seeder for this file + origin, ok = ft.FileLeechers.Load(message.FileUUID) + if !ok { + syncFile, err := ft.Library.GetFile(message.FileUUID) + if err != nil { + return fmt.Errorf("can't find file %s", message.FileUUID) + } + // careful : need to be thread safe + fmt.Println("ReceiveAskChunk", message.FileName, "create new seeder") + err = ft.addNewSeeder(syncFile, conn, message) + return err + } + + leecher := origin.(*FileLeecher) + if leecher.haveChunk(message.Begin, message.Length) { + //fmt.Println("I have the chunk", message.Begin) + err := leecher.sendBlock(conn, message.Begin, message.Length) + conn.GetLogger().Trace().Msgf("leecher send block %d,%d,%v", message.Begin, message.Length, err) + return err + } else { + return fmt.Errorf("i don't have the chunk %d", message.Begin) + } + } else { + err := origin.(*FileSeeder).sendBlock(conn, message.Begin, message.Length) + conn.GetLogger().Trace().Msgf("seeder send block %d,%d,%v", message.Begin, message.Length, err) + return err + } + } else { + return fmt.Errorf("not authorised") + } +} + +func (ft *FileTransferImpl) addNewSeeder(syncFile SyncFile, conn ShosetConn, message *msg.FileMessage) error { + ft.m.Lock() + origin, ok := ft.FileSeeders.Load(syncFile.GetUUID()) + var newSeeder *FileSeeder + if ok { + newSeeder = origin.(*FileSeeder) + } else { + newSeeder = new(FileSeeder) + newSeeder.InitSeeder(syncFile, ft) + ft.FileSeeders.Store(syncFile.GetUUID(), newSeeder) + } + ft.m.Unlock() + err := newSeeder.sendBlock(conn, message.Begin, message.Length) + return err +} + +func (ft *FileTransferImpl) ReceiveAskInfoMessage(conn ShosetConn, message *msg.FileMessage) { + syncFile, err := ft.Library.GetFile(message.FileUUID) + if err != nil { + conn.GetLogger().Warn().Msgf("%v", err) + return + } + ft.SendMessage(syncFile.GetFullInfoMsg(), conn) +} + +func (ft *FileTransferImpl) GetMissingLength() int64 { + ft.m.Lock() + defer ft.m.Unlock() + return ft.missingLength +} + +func (ft *FileTransferImpl) DecreaseMissingLength(length int) { + ft.m.Lock() + defer ft.m.Unlock() + ft.missingLength = Max64(ft.missingLength-int64(length), 0) +} + +// keep asking info if we don't have this file +// to launch in a goroutine +func (ft *FileTransferImpl) AskInfoFile(conn ShosetConn, fileState FileState) { + ft.m.Lock() + + leecher, ok := ft.FileLeechers.Load(fileState.UUID) + if ok { // if we already have a leacher for this file, we don't need to ask info + if leecher.(*FileLeecher).GetHash() == fileState.Hash { // if the hash is the same, we don't need to ask info + leecher.(*FileLeecher).InitDownload(conn) + ft.m.Unlock() + return + } + } + + ft.GetInfoMap[fileState.UUID] = fileState.Hash + + // we ask info for this file + ft.m.Unlock() + count := 0 + for count < 2 { // we ask 2 times, after we give up + ft.m.Lock() + hash, ok := ft.GetInfoMap[fileState.UUID] + ft.m.Unlock() + if !ok || hash != fileState.Hash { // if the hash is not the same, we don't need to ask info + return + } + // we need to keep asking info + message := msg.FileMessage{MessageName: "askInfo", + FileName: fileState.Name, + FileHash: fileState.Hash, + FileUUID: fileState.UUID, + } + message.InitMessageBase() + ft.SendMessage(message, conn) + time.Sleep(10 * time.Second) + count++ + } +} + +func (ft *FileTransferImpl) checkRequestTimeout() { + ft.FileLeechers.Range(func(_, value interface{}) bool { + value.(*FileLeecher).CheckRequestTimeout() + return true + }) +} + +func (ft *FileTransferImpl) watchRequestsTimeout(nbMilliSec int) { + sleepDuration := time.Duration(nbMilliSec) * time.Millisecond + ticker := time.NewTicker(sleepDuration) + + go func() { + for { + select { + case <-ticker.C: + ft.checkRequestTimeout() + case <-ft.stop: + ticker.Stop() + return + + } + } + }() +} + +func (ft *FileTransferImpl) InitLeecher(syncFile SyncFile, conn ShosetConn) { + leecher, ok := ft.FileLeechers.Load(syncFile.GetUUID()) + if !ok { // we don't have a leecher for this file + newFileLeecher := NewFileLeecher(syncFile, ft) + ft.AddFileLeecher(newFileLeecher) + + } else { + // we modify the leecher (for instance when there is another version and we didn't finished the download) + leecher.(*FileLeecher).UpdateLeeching() + } + syncFile.SetStatus("downloading") +} + +func (ft *FileTransferImpl) ReceiveMessage(message *msg.FileMessage, conn ShosetConn) { + var messageQueue *MessageQueue + messageQueueI, ok := ft.ReceiveQueue.Load(conn) + if !ok { + // we do the same but inside a lock to avoid multiple go routine to create the same message queue + ft.m.Lock() + messageQueueI, ok := ft.ReceiveQueue.Load(conn) + if !ok { + messageQueue = NewMessageQueue() + go ft.HandleReceiveMessage(messageQueue, conn) + ft.ReceiveQueue.Store(conn, messageQueue) + } else { + messageQueue = messageQueueI.(*MessageQueue) + } + ft.m.Unlock() + } else { + messageQueue = messageQueueI.(*MessageQueue) + } + messageQueue.Push(message) +} + +func (ft *FileTransferImpl) HandleReceiveMessage(messageQueue *MessageQueue, conn ShosetConn) { + + for { + select { + case <-messageQueue.GetChan(): + message := messageQueue.Pop() + if message == nil { + return + } + err := ft.HandleReceiveMessageFromQueue(*message, conn) + if err != nil { + conn.GetLogger().Warn().Msgf("%v", err) + // TODO : change the way to handle errors + } + + case <-ft.stop: + return + } + } +} + +func (ft *FileTransferImpl) HandleReceiveMessageFromQueue(fileMessage msg.FileMessage, c ShosetConn) error { + switch fileMessage.MessageName { + case "authorised": + ft.ReceiveAuthorisedMessage(c) + case "unauthorised": + ft.ReceiveUnauthorisedMessage(c) + case "congestion": + ft.ReceiveCongestionMessage(c) + case "askInfo": + ft.ReceiveAskInfoMessage(c, &fileMessage) + case "interested": + ft.ReceiveInterestedMessage(c, &fileMessage) + case "notInterested": + ft.ReceiveInterestedMessage(c, &fileMessage) + case "have": + ft.ReceiveHaveMessage(c, &fileMessage) + case "sendBitfield": + ft.ReceiveBitfieldMessage(c, &fileMessage) + case "askBitfield": + ft.ReceiveAskBitfieldMessage(c, &fileMessage) + + case "sendInfo": + fileState := FileState{ + UUID: fileMessage.FileUUID, + Name: fileMessage.FileName, + Hash: fileMessage.FileHash, + HashMap: fileMessage.FileHashMap, + Version: fileMessage.FileVersion, + LastOperation: ToOperation(fileMessage.FileOperation), + } + // we receive some information about a file (probably new or have been modified) + ft.m.Lock() + syncFile, err := ft.Library.GetFile(fileMessage.FileUUID) + if err != nil { // we don't have this file in our library + if len(fileMessage.FileHashMap) == 0 { + // we ask for more info + ft.m.Unlock() + go ft.AskInfoFile(c, fileState) + } else { + // we create the Copy file to be ready to receive the chunks + CopyFile, err := NewEmptyFile(ft.Library.GetDir(), RealToCopyPath(fileMessage.FilePath), fileMessage.FileName, fileMessage.FileSize, fileMessage.FileHash, fileMessage.FileVersion, fileMessage.FileHashMap) + if err != nil { + return err + } + // create the syncFile and add it to the library + syncFile, err := ft.Library.CreateFile(CopyFile, fileMessage.FileUUID) + if err != nil { + return err + } + ft.m.Unlock() + // we start the download + + ft.InitLeecher(syncFile, c) + + // normally a new leecher is created + leecher, ok := ft.FileLeechers.Load(fileMessage.FileUUID) + if !ok { + return fmt.Errorf("leecher not found for file %s", fileMessage.FileUUID) + } + leecher.(*FileLeecher).InitDownload(c) + shortInfo := syncFile.GetShortInfoMsg() + ft.Broadcast(&shortInfo) + } + } else { // we already have this file in our library + ft.m.Unlock() + syncFile.UpdateFile(fileState, c) + } + + case "sendChunk": + // we receive a chunk of the file + // for now : no verification if we asked it (potential security issue) + err := ft.ReceiveChunk(c, &fileMessage) + if err != nil { + ft.Logger.Error().Err(err).Msg("error while receiving chunk") + } + + case "askChunk": + // we receive a request for a chunk of the file + // we send it back + _, ok := ft.AuthorisedConn.Load(c) + if !ok { + ft.AddUploadConn(c) + } + err := ft.ReceiveAskChunk(c, &fileMessage) + if err != nil { + ft.Logger.Error().Err(err).Msg("error while receiving askChunk") + return err + } + case "sendLibrary": + // someone notify us its library + // we should check if it's from the same Lname + + //c.Logger.Debug().Msg("sendLibrary") + listFileState := make([]FileState, 0) + for _, fileStateMsg := range fileMessage.Library { + listFileState = append(listFileState, ToFileState(fileStateMsg)) + } + // we update our library + ft.Library.UpdateLibrary(listFileState, c) + case "askLibraryLocked": + // someone ask us if we have a locked the library + answer := ft.ExternalCommands.IsLocked(&fileMessage) + answer.FileHash = ft.Library.GetHash() + ft.SendMessage(answer, c) + case "answerLibraryLocked": + // someone answer us if we have a locked the library + //fmt.Println("comparing hash library", fileMessage.FileHash, ft.Library.GetHash(), fileMessage.AnswerLocked) + if fileMessage.FileHash != ft.Library.GetHash() { + // we have a different library + c.GetLogger().Info().Msg("Received Librarylock message but we have a different library") + fileMessage.AnswerLocked = true + libMsg, err := ft.Library.GetMessageLibrary() + if err != nil { + return err + } + ft.SendMessage(*libMsg, c) + } + ft.ExternalCommands.ReceiveAnswer(&fileMessage, c.GetRemoteAddress(), ft.nbConn) + default: + return errors.New("Unknown message name for file : " + fileMessage.MessageName) + } + return nil +} + +func (ft *FileTransferImpl) SendMessage(message msg.FileMessage, conn ShosetConn) { + if message.MessageName == "sendChunk" { + message.MissingLength = ft.missingLength + } + + var messageQueue *MessageQueue + messageQueueI, ok := ft.SendQueue.Load(conn) + if !ok { + // we do the same but inside a lock to avoid multiple go routine to create the same message queue + ft.m.Lock() + messageQueueI, ok := ft.SendQueue.Load(conn) + if !ok { + //fmt.Println(conn.GetLocalAddress(), "creating message queue", conn.GetRemoteAddress()) + messageQueue = NewMessageQueue() + go ft.HandleSendMessage(messageQueue, conn) + ft.SendQueue.Store(conn, messageQueue) + } else { + messageQueue = messageQueueI.(*MessageQueue) + } + ft.m.Unlock() + } else { + messageQueue = messageQueueI.(*MessageQueue) + } + messageQueue.Push(&message) +} + +func (ft *FileTransferImpl) HandleSendMessage(messageQueue *MessageQueue, conn ShosetConn) { + for { + select { + case <-messageQueue.GetChan(): + message := messageQueue.Pop() + if message == nil { + return + } + err := ft.HandleSendMessageFromQueue(*message, conn) + if err != nil { + fmt.Println(err) + // TODO : change the way to handle errors + } + + case <-ft.stop: + return + } + } +} + +func (ft *FileTransferImpl) HandleSendMessageFromQueue(fileMessage msg.FileMessage, c ShosetConn) error { + return c.SendMessage(fileMessage) +} + +func (ft *FileTransferImpl) SetExternalCommands(ec *ExternalCommands) { + ft.ExternalCommands = ec +} + +func (ft *FileTransferImpl) SetNbConn(nbConn int) { + ft.m.Lock() + defer ft.m.Unlock() + ft.nbConn = nbConn +} + +func (ft *FileTransferImpl) GetLibrary() FileLibrary { + return ft.Library +} + +func (ft *FileTransferImpl) Broadcast(message *msg.FileMessage) { + ft.broadcast(message) +} + +func (ft *FileTransferImpl) UserPush(message *msg.FileMessage) { + ft.userMessageQueue.Push(message, "", "") +} + +func (ft *FileTransferImpl) DeleteLeecher(fileUUID string) { + ft.FileLeechers.Delete(fileUUID) +} + +func (ft *FileTransferImpl) GetLeecher(fileUUID string) *FileLeecher { + leecher, ok := ft.FileLeechers.Load(fileUUID) + if !ok { + return nil + } + return leecher.(*FileLeecher) +} + +func (ft *FileTransferImpl) WriteRecords() { + ft.LogRecords.writeRecords() +} + +func (ft *FileTransferImpl) GetLogger() *zerolog.Logger { + return &ft.Logger +} + +func (ft *FileTransferImpl) GetReceiveQueue(conn *ShosetConn) *MessageQueue { + messageQueueI, ok := ft.ReceiveQueue.Load(*conn) + if !ok { + return nil + } + return messageQueueI.(*MessageQueue) +} diff --git a/file/logRateTransfer.go b/file/logRateTransfer.go new file mode 100644 index 0000000..9039ef3 --- /dev/null +++ b/file/logRateTransfer.go @@ -0,0 +1,110 @@ +package fileSync + +import ( + "encoding/csv" + "fmt" + "log" + "os" + "strconv" + "sync" +) + +/* +It stores in a csv file the flowrate of each connection. +Initialy, it was used to store the upload and download rate. +With the new variable used to monitor the flowrate, it is only storing this variable : the RTT of each request. +*/ + +type LogRateTransfer struct { + rateMap map[int64]map[ShosetConn]int // map[conn]map[timestamp][uploadRate, downloadRate] + connMap map[ShosetConn]bool + path string + file *os.File + writer *csv.Writer + m sync.RWMutex + + titleWritten bool +} + +func NewLogRateTransfer(path string) *LogRateTransfer { + return &LogRateTransfer{ + rateMap: make(map[int64]map[ShosetConn]int), + connMap: make(map[ShosetConn]bool), + path: path, + } +} + +func (logRateTransfer *LogRateTransfer) createMonitorFile() { + logRateTransfer.m.Lock() + defer logRateTransfer.m.Unlock() + file, err := os.Create(logRateTransfer.path) + if err != nil { + log.Fatalln("failed to open file", err) + } + logRateTransfer.file = file + logRateTransfer.writer = csv.NewWriter(file) +} + +// append the flowrates to the file. +// it empty the map that srtores this rates +func (logRateTransfer *LogRateTransfer) writeRecords() error { + logRateTransfer.m.Lock() + defer logRateTransfer.m.Unlock() + fmt.Println("writeRecords", logRateTransfer.connMap) + var err error + if !logRateTransfer.titleWritten { + title := []string{"timestamp"} + for conn := range logRateTransfer.connMap { + title = append(title, conn.GetRemoteAddress()+"_uploadRate", conn.GetRemoteAddress()+"_downloadRate") + } + err = logRateTransfer.writer.Write(title) + if err != nil { + return err + } + logRateTransfer.writer.Flush() + logRateTransfer.titleWritten = true + } + for timestamp, connMap := range logRateTransfer.rateMap { + record := []string{strconv.FormatInt(timestamp, 10)} + count := 0 + for conn := range logRateTransfer.connMap { + rate, ok := connMap[conn] + if ok { + count += rate + record = append(record, setNA(rate)) + } else { + record = append(record, "") + } + } + if count == 0 { + // there is nothing to write + continue + } + err = logRateTransfer.writer.Write(record) + if err != nil { + return err + } + logRateTransfer.writer.Flush() + } + logRateTransfer.rateMap = make(map[int64]map[ShosetConn]int) + return nil //logRateTransfer.file.Close() +} + +func (logRateTransfer *LogRateTransfer) AddRateConn(timestamp int64, conn ShosetConn, RTT int) { + logRateTransfer.m.Lock() + defer logRateTransfer.m.Unlock() + connMap, ok := logRateTransfer.rateMap[timestamp] + if !ok { + connMap = make(map[ShosetConn]int) + logRateTransfer.rateMap[timestamp] = connMap + } + connMap[conn] = RTT + logRateTransfer.connMap[conn] = true +} + +func setNA(i int) string { + if i == 0 { + return "" + } + return strconv.Itoa(i) +} diff --git a/file/operations.go b/file/operations.go new file mode 100644 index 0000000..48d2040 --- /dev/null +++ b/file/operations.go @@ -0,0 +1,47 @@ +package fileSync + +import ( + "github.com/ditrit/shoset/msg" +) + +/* +This file is used to describe an operation on a file +*/ + +type Operation struct { + File string // relative path and name of the file + Name string // name of the operation + NewFile string // in case of a renamed or a moved file + Version int // version of the file + Hash string // hash of the file +} + +// get the message format of an operation +func (operation Operation) OperationMessage() msg.OperationMessage { + return msg.OperationMessage{ + Name: operation.Name, + File: operation.File, + NewFile: operation.NewFile, + Version: operation.Version, + Hash: operation.Hash, + } +} + +// convert an operationMessage to an operation +func ToOperation(operation msg.OperationMessage) Operation { + return Operation{ + File: operation.File, + Name: operation.Name, + NewFile: operation.NewFile, + Version: operation.Version, + Hash: operation.Hash, + } +} + +func (operation Operation) String() string { + if operation.NewFile != "" { + return operation.Name + " " + operation.File + " into " + operation.NewFile + } else { + return operation.Name + " " + operation.File + } +} diff --git a/file/piece.go b/file/piece.go new file mode 100644 index 0000000..b247ed1 --- /dev/null +++ b/file/piece.go @@ -0,0 +1,261 @@ +package fileSync + +import ( + "sync" + "time" + + "github.com/ditrit/shoset/msg" +) + +type Piece struct { + SyncFile SyncFile + File File + pieceId int + hash string // hash of the piece + pieceSize int // size in byte of the piece + blockSize int // size in byte of a block + blocks [][CHUNKSIZE]byte // blocks of the piece + blockPossessed []bool // bitfield of the blocks we have + nbBlocksPossessed int // number of blocks we have + blockRequested []bool // list of the blocks we have already requested + blockTimestamp []int64 // timestamp of the last request of the block + nbBlocks int // number of blocks in the piece + noBlock bool // true if we download the piece at one time (we are not using blocks) + data []byte // data of the piece + pieceTimestamp int64 // timestamp of the last request of the piece + + ConnInformation ConnInfo // information about the connection we are downloading the piece from + + m sync.Mutex +} + +func NewPiece(syncFile SyncFile, pieceId int, pieceSize int, hash string, connInfo ConnInfo) *Piece { + var piece Piece + piece.SyncFile = syncFile + piece.File = syncFile.GetCopyFile() + piece.pieceId = pieceId + piece.pieceSize = pieceSize + piece.hash = hash + piece.blockSize = CHUNKSIZE + piece.nbBlocks = pieceSize / CHUNKSIZE + piece.blocks = make([][CHUNKSIZE]byte, piece.nbBlocks) + piece.blockPossessed = make([]bool, piece.nbBlocks) + piece.nbBlocksPossessed = 0 + piece.blockRequested = make([]bool, piece.nbBlocks) // false by default + piece.blockTimestamp = make([]int64, piece.nbBlocks) + piece.ConnInformation = connInfo + return &piece +} + +func (piece *Piece) GetNoBlock() bool { + piece.m.Lock() + defer piece.m.Unlock() + return piece.noBlock +} + +func (piece *Piece) SetNoBlock(noBlock bool) { + piece.m.Lock() + defer piece.m.Unlock() + piece.noBlock = noBlock +} + +func (piece *Piece) SetData(data []byte) { + piece.m.Lock() + defer piece.m.Unlock() + piece.data = data +} + +func (piece *Piece) AddBlockToPiece(blockId int, block []byte) bool { + piece.m.Lock() + defer piece.m.Unlock() + if piece.blockPossessed[blockId] { + return false + } + copy(piece.blocks[blockId][:], block) + piece.blockPossessed[blockId] = true + piece.blockRequested[blockId] = false + piece.nbBlocksPossessed++ + return true +} + +func (piece *Piece) IsComplete() bool { + piece.m.Lock() + defer piece.m.Unlock() + return piece.nbBlocksPossessed == piece.nbBlocks +} + +func (piece *Piece) GetId() int { + piece.m.Lock() + defer piece.m.Unlock() + return piece.pieceId +} + +func (piece *Piece) getNextRequestedBlock() int { + for i, blockPossessed := range piece.blockPossessed { + if !blockPossessed && !piece.blockRequested[i] { + piece.blockRequested[i] = true + piece.blockTimestamp[i] = time.Now().UnixMilli() + return i + } + } + return -1 +} + +func (piece *Piece) WritePiece() error { + piece.m.Lock() + defer piece.m.Unlock() + if len(piece.data) > 0 { // if the piece is entirely downloaded in onte itime and the data is in piece.data + err := piece.File.WriteChunk(piece.data, int64(piece.pieceId)*int64(piece.pieceSize)) + return err + } + data := []byte{} + for i := 0; i < piece.nbBlocks; i++ { + data = append(data, piece.blocks[i][:]...) + } + return piece.File.WriteChunk(data, int64(piece.pieceId)*int64(piece.pieceSize)) +} + +// return true if we made all the block requests and the connInformation say that we can still ask for more, false otherwise +func (piece *Piece) downloadNextBlocks() bool { + piece.m.Lock() + count := 0 + if piece.noBlock { + return false + } + for piece.ConnInformation.CanRequestBlock() && count < 2 { + + blockId := piece.getNextRequestedBlock() + if blockId == -1 { // we reached the end of the piece, there is no more block to requests + piece.m.Unlock() + //piece.ConnInformation.Leecher.DownloadNextPieces(*piece.ConnInformation.Conn) + return true + } + piece.SyncFile.GetUUID() + piece.File.GetName() + message := msg.FileMessage{ + MessageName: "askChunk", + FileUUID: piece.SyncFile.GetUUID(), + FileName: piece.File.GetName(), + FileHash: piece.File.GetHash(), + Begin: int64(piece.pieceId)*int64(piece.pieceSize) + int64(blockId*piece.blockSize), + Length: piece.blockSize, + } + message.InitMessageBase() + piece.ConnInformation.GetLeecher().FileTransfer.AskChunk(piece.ConnInformation.GetConn(), &message, !piece.ConnInformation.HadARecentDecrease()) + piece.ConnInformation.AddRequestBlock() + //fmt.Println(conn.GetLocalAddress(), "asking piece", piece.pieceId, "to", conn.GetRemoteAddress()) + count++ + } + piece.m.Unlock() + return false +} + +func (piece *Piece) downloadPiece() { + piece.m.Lock() + defer piece.m.Unlock() + if piece.noBlock { + //fmt.Println(conn.GetLocalAddress(), "asking piece", piece.pieceId, "to", conn.GetRemoteAddress()) + piece.pieceTimestamp = time.Now().UnixMilli() + message := msg.FileMessage{ + MessageName: "askChunk", + FileUUID: piece.SyncFile.GetUUID(), + FileName: piece.File.GetName(), + FileHash: piece.File.GetHash(), + Begin: int64(piece.pieceId) * int64(piece.pieceSize), + Length: piece.pieceSize, + } + message.InitMessageBase() + piece.ConnInformation.GetLeecher().FileTransfer.AskChunk(piece.ConnInformation.GetConn(), &message, !piece.ConnInformation.HadARecentDecrease()) + } +} + +// return true if a timeout occured +func (piece *Piece) CheckRequestTimeout() bool { + piece.m.Lock() + timeoutOccured := false + conn := piece.ConnInformation.GetConn() + conn.GetLogger().Trace().Msgf("%v timeout check for piece %d", conn.GetLocalAddress(), piece.pieceId) + if piece.noBlock { + if time.Now().UnixMilli()-piece.pieceTimestamp > CHUNKTIMEOUT { + conn := piece.ConnInformation.GetConn() + conn.GetLogger().Trace().Msgf("%v --------------------- timeout for piece %d / %d ---------------- asked to %v", conn.GetLocalAddress(), piece.pieceId, int64(piece.pieceId)*int64(piece.pieceSize), conn.GetRemoteAddress()) + piece.m.Unlock() + piece.ConnInformation.DecreaseLevel() + piece.ConnInformation.GetLeecher().AddToRequestPriority(piece.pieceId, piece.ConnInformation) + piece.m.Lock() + timeoutOccured = true + } + } else { + for i, blockPossessed := range piece.blockPossessed { + if !blockPossessed && piece.blockRequested[i] { + if time.Now().UnixMilli()-piece.blockTimestamp[i] > CHUNKTIMEOUT { + conn := piece.ConnInformation.GetConn() + conn.GetLogger().Trace().Msgf("%v --------------------- timeout for piece %d and block %d / %d ---------------- asked to %v", conn.GetLocalAddress(), piece.pieceId, i, int64(piece.pieceId)*int64(piece.pieceSize), conn.GetRemoteAddress()) + go func() { + // we remove this request 3s after, if we didn't received it + time.Sleep(3 * time.Second) + piece.m.Lock() + messageQueue := piece.ConnInformation.GetLeecher().FileTransfer.GetReceiveQueue(&conn) + if messageQueue != nil { + if messageQueue.HasChunk(int64(piece.pieceId)*int64(piece.pieceSize) + int64(i*piece.blockSize)) { + // if we received the message but didn't treated it + piece.m.Unlock() + piece.ConnInformation.DecreaseLevel() + return + } + } + if !piece.blockPossessed[i] && piece.blockRequested[i] { + conn.GetLogger().Trace().Msgf("reasking %d and block %d / %d ---------------- asked to %v", piece.pieceId, i, int64(piece.pieceId)*int64(piece.pieceSize)+int64(i*piece.blockSize), conn.GetRemoteAddress()) + piece.blockRequested[i] = false + piece.m.Unlock() + piece.ConnInformation.RemoveRequestBlock() + piece.ConnInformation.DecreaseLevel() + } else { + piece.m.Unlock() + } + }() + timeoutOccured = true + } + if time.Now().UnixMilli()-piece.blockTimestamp[i] > 10000 { // it's been 10s since we asked for this block, we remove the request for the entire piece + piece.m.Unlock() + piece.ConnInformation.DecreaseLevel() + piece.ConnInformation.GetLeecher().AddToRequestPriority(piece.pieceId, piece.ConnInformation) + piece.m.Lock() + for i, blockPossessed := range piece.blockPossessed { + if !blockPossessed && piece.blockRequested[i] { + piece.blockRequested[i] = false + piece.ConnInformation.RemoveRequestBlock() + } + } + break + } + } + } + } + piece.m.Unlock() + return timeoutOccured +} + +func (piece *Piece) CheckHash() bool { + piece.m.Lock() + defer piece.m.Unlock() + if len(piece.data) > 0 { + return piece.hash == Hash(piece.data) + } + + data := []byte{} + for i := 0; i < piece.nbBlocks; i++ { + data = append(data, piece.blocks[i][:]...) + } + if piece.hash != Hash(data) { + conn := piece.ConnInformation.GetConn() + conn.GetLogger().Trace().Msgf("%d hash not matching %v : %v", piece.pieceId, piece.hash, Hash(data)) + } + return piece.hash == Hash(data) +} + +func (piece *Piece) SetConnInfo(connInfo ConnInfo) { + piece.m.Lock() + defer piece.m.Unlock() + piece.ConnInformation = connInfo +} diff --git a/file/syncFile.go b/file/syncFile.go new file mode 100644 index 0000000..34b8047 --- /dev/null +++ b/file/syncFile.go @@ -0,0 +1,483 @@ +package fileSync + +import ( + "encoding/json" + "fmt" + "io" + "log" + "os" + "path/filepath" + "sync" + + "github.com/ditrit/shoset/msg" + guuid "github.com/kjk/betterguid" +) + +/* +It makes the link between the real file (the file in the library) and the copy file (the file in the .copy folder). +Each SyncFile has a uuid (unique identifier). +This uuid is used to identify the file we are talking about. +It is usefull when a file is renamed. +SyncFile is also used to keep track of the operations made on the file and to update the files in both the library or the library copy. +*/ + +var opMapScore = map[string]int{"remove": 0, "move": 1, "modify": 2, "create": 3} + +type FileState struct { + UUID string + Name string + Hash string + HashMap map[int]string + Version int + Path string + LastOperation Operation +} + +// convert a Filstate structure to a msg.FileStateMessage +func (fs FileState) FileStateMessage() msg.FileStateMessage { + return msg.FileStateMessage{ + UUID: fs.UUID, + Name: fs.Name, + Hash: fs.Hash, + Version: fs.Version, + Path: fs.Path, + LastOperation: fs.LastOperation.OperationMessage(), + } +} + +// convert a msg.FileStateMessage structure to a Filstate +func ToFileState(message msg.FileStateMessage) FileState { + return FileState{ + UUID: message.UUID, + Name: message.Name, + Hash: message.Hash, + Version: message.Version, + Path: message.Path, + LastOperation: ToOperation(message.LastOperation), + } +} + +type SyncFile interface { + String() string + EndDownload() error + WriteCopyToReal() error + WriteRealToCopy() error + // when there is a change on the real file, do it to the Copy file + ApplyOperationFromMe(op Operation) error + UpdateFile(fileState FileState, conn ShosetConn) error + SaveFileInfo() error + GetRealFile() File + GetCopyFile() File + GetUUID() string + SetUUID(uuid string) + SetFileTransfer(ft FileTransfer) + GetLastOperation() Operation + GetShortInfoMsg() msg.FileMessage + // like shortInfo but we add the hashmap + GetFullInfoMsg() msg.FileMessage + GetFileState() FileState + SetStatus(status string) + GetStatus() string +} + +type SyncFileImpl struct { + RealFile File // file in the library + CopyFile File // copy of the file in the library (it is in the .copy folder) + baseFilesDir string // path to the library + + uuid string // unique identifier of the file + lastOperation Operation // last Operation done on the file (usefull to resolve conflicts if it occurs) + FileTransfer FileTransfer // FileTransfer object to send the file to other nodes + + /* + Different status : + downloading : the File data is being downloaded + full : Data is loaded + */ + status string //empty, full + synced bool // true if the file is synced with the other nodes + m sync.RWMutex +} + +func NewSyncFile(baseFilesDir string) *SyncFileImpl { + return &SyncFileImpl{ + baseFilesDir: baseFilesDir, + status: "empty", + synced: false, + uuid: guuid.New(), + } +} + +func (syncFile *SyncFileImpl) String() string { + syncFile.m.RLock() + defer syncFile.m.RUnlock() + var result string + result += "Name : " + syncFile.RealFile.GetName() + "\n" + result += "UUID : " + syncFile.uuid + "\n" + result += "Path : " + filepath.Join(syncFile.baseFilesDir, syncFile.RealFile.GetRelativePath(), syncFile.RealFile.GetName()) + "\n" + result += "Size: " + fmt.Sprint(syncFile.RealFile.GetSize()) + "\n" + result += "status : " + syncFile.status + "\n" + + return result +} + +func (syncFile *SyncFileImpl) EndDownload() error { + syncFile.SaveFileInfo() + syncFile.SetStatus("full") + syncFile.CopyFile.CloseFile() + newHash, err := syncFile.CopyFile.CalculateHash() + if err != nil { + return err + } + if newHash != syncFile.CopyFile.GetHash() { + hashMap := syncFile.CopyFile.GetHashMap() + newHashMap, err := syncFile.CopyFile.CalculateHashMap() + if err != nil { + return err + } + idList := make([]int, 0) + for i := 0; i < len(hashMap); i++ { + if hashMap[i] != newHashMap[i] { + idList = append(idList, i) + } + } + return fmt.Errorf("hash of the file "+syncFile.CopyFile.GetName()+" is not correct because of piece(s)", idList) + } + return err +} + +func (syncFile *SyncFileImpl) WriteCopyToReal() error { + syncFile.m.Lock() + defer syncFile.m.Unlock() + syncFile.RealFile.CloseFile() + syncFile.CopyFile.CloseFile() + CopyFile, err := syncFile.CopyFile.OpenFile() + if err != nil { + return err + } + realFile, err := syncFile.RealFile.OpenFile() + if err != nil { + return err + } + _, err = io.Copy(realFile, CopyFile) + if err != nil { + return err + } + syncFile.RealFile.CloseFile() + syncFile.CopyFile.CloseFile() + err = syncFile.RealFile.UpdateMetadata() + return err +} + +func (syncFile *SyncFileImpl) WriteRealToCopy() error { + syncFile.m.Lock() + defer syncFile.m.Unlock() + syncFile.RealFile.CloseFile() + syncFile.CopyFile.CloseFile() + CopyFile, err := syncFile.CopyFile.OpenFile() + if err != nil { + return err + } + realFile, err := syncFile.RealFile.OpenFile() + if err != nil { + return err + } + _, err = io.Copy(CopyFile, realFile) + if err != nil { + return err + } + syncFile.RealFile.CloseFile() + syncFile.CopyFile.CloseFile() + err = syncFile.CopyFile.UpdateMetadata() + return err +} + +// when there is a change on the real file, do it to the Copy file +func (syncFile *SyncFileImpl) ApplyOperationFromMe(op Operation) error { + syncFile.m.Lock() + defer syncFile.m.Unlock() + if op.NewFile != "" { + syncFile.RealFile.SetRelativePath(Dir(op.NewFile)) + syncFile.RealFile.SetName(filepath.Base(op.NewFile)) + syncFile.RealFile.SetVersion(syncFile.RealFile.GetVersion() + 1) + } + + if syncFile.status == "downloading" { + return fmt.Errorf("the file %s is being downloaded", syncFile.RealFile.GetName()) + } + + // the operation is normally valid and can occur. We apply it + if op.Name == "create" { + syncFile.lastOperation = op + create := syncFile.getShortInfoMsg() + syncFile.FileTransfer.Broadcast(&create) + } else if op.Name == "remove" { + // we remove the leechers for the file (if some were added meanwhile) and the file from the library : it no longer exists + //syncFile.FileTransfer.Library.DeleteFile(syncFile.CopyFile.GetName()) + syncFile.FileTransfer.DeleteLeecher(syncFile.GetUUID()) + } else if op.Name == "modify" { // the file has beeen renamed or moved + err := syncFile.RealFile.UpdateMetadata() + if err != nil { + return err + } + + if syncFile.RealFile.GetHash() != syncFile.CopyFile.GetHash() { // if the file has changed + err := syncFile.WriteRealToCopy() + if err != nil { + return err + } + err = syncFile.CopyFile.UpdateMetadata() + if err != nil { + return err + } + } + if op.NewFile != "" { + err := syncFile.CopyFile.Move(filepath.Join(PATH_COPY_FILES, Dir(op.NewFile)), filepath.Base(op.NewFile)) + if err != nil { + return err + } + } + syncFile.CopyFile.SetVersion(op.Version) + } + syncFile.lastOperation = op + return nil +} + +// when there is a chnage on another node, we apply it on the Copy file, then on the real file if possible +func (syncFile *SyncFileImpl) applyOperationFromOther(fileState FileState, conn ShosetConn) error { + op := fileState.LastOperation + if op.Version < syncFile.CopyFile.GetVersion() { + // we ignore the operation because it is older than the one we already have + // we send him the info of our file (shorter version) + syncFile.FileTransfer.SendMessage(syncFile.getShortInfoMsg(), conn) + //fmt.Println(fileState.UUID, ": upate file :", "nothing to do") + return nil + } + if op.Version == syncFile.CopyFile.GetVersion() { + if opMapScore[op.Name] < opMapScore[syncFile.lastOperation.Name] { + // we ignore the operation because it is older than the one we already have + // we send him the info of our file (short version) + syncFile.FileTransfer.SendMessage(syncFile.getShortInfoMsg(), conn) + //fmt.Println(fileState.UUID, ": upate file :", "nothing to do") + return nil + } else if opMapScore[op.Name] == opMapScore[syncFile.lastOperation.Name] { + // this is the same type of operation, we keep the one with a smaller hash + if op.Hash+op.NewFile > syncFile.lastOperation.Hash+syncFile.lastOperation.NewFile { + // we ignore the operation because our hash of the last operation is smaller + // we send him the info of our file (short version) + syncFile.FileTransfer.SendMessage(syncFile.getShortInfoMsg(), conn) + //fmt.Println(fileState.UUID, ": upate file :", "nothing to do") + return nil + } else if op.Hash+op.NewFile == syncFile.lastOperation.Hash+syncFile.lastOperation.NewFile { + // do nothing because same version, same operation, same hash + //fmt.Println(fileState.UUID, ": upate file :", "nothing to do") + return nil + } + } + } + // if we arrive here, it means that we must apply the incoming operation + if op.Name == "remove" { + //syncFile.FileTransfer.Library.DeleteFile(syncFile.CopyFile.GetName()) + syncFile.FileTransfer.DeleteLeecher(syncFile.GetUUID()) + log.Println(fileState.UUID, ": upate file :", "remove file") + } else if op.Name == "modify" { // the file has beeen renamed or moved + + if op.NewFile != filepath.Join(syncFile.CopyFile.GetRelativePath(), syncFile.CopyFile.GetName()) && op.NewFile != "" { + // we rename directly the file + err := syncFile.CopyFile.Move(filepath.Join(PATH_COPY_FILES, Dir(op.NewFile)), filepath.Base(op.NewFile)) + if err != nil { + return err + } + err = syncFile.RealFile.Move(Dir(op.NewFile), filepath.Base(op.NewFile)) + if err != nil { + // we move back the Copy file + syncFile.CopyFile.Move(filepath.Join(PATH_COPY_FILES, Dir(op.File)), filepath.Base(op.File)) + return err + } + } + if op.Hash != syncFile.CopyFile.GetHash() { + if len(fileState.HashMap) != 0 { + syncFile.CopyFile.SetHash(fileState.Hash) + syncFile.CopyFile.SetHashMap(fileState.HashMap) + syncFile.m.Unlock() + syncFile.FileTransfer.InitLeecher(syncFile, conn) + syncFile.m.Lock() + log.Println(fileState.UUID, ": upate file :", "init leecher") + } else { + log.Println(fileState.UUID, ": upate file :", "asking info") + go syncFile.FileTransfer.AskInfoFile(conn, fileState) + } + } + syncFile.CopyFile.SetVersion(fileState.Version) + } + return nil +} + +func (syncFile *SyncFileImpl) UpdateFile(fileState FileState, conn ShosetConn) error { + syncFile.m.Lock() + defer syncFile.m.Unlock() + //fmt.Println("comparing \n", conn.GetRemoteAddress(), fileState, "\n", conn.GetLocalAddress(), syncFile.CopyFile.GetVersion(), syncFile.CopyFile.GetHash(), syncFile.CopyFile.GetName(), syncFile.RealFile.GetRelativePath()) + var err error + if (fileState.Version != syncFile.CopyFile.GetVersion()) || (fileState.Hash != syncFile.CopyFile.GetHash()) || (fileState.Name != syncFile.CopyFile.GetName()) || (fileState.Path != syncFile.RealFile.GetRelativePath()) { + err = syncFile.applyOperationFromOther(fileState, conn) + return err + } else { + if syncFile.status == "downloading" { + leecher := syncFile.FileTransfer.GetLeecher(syncFile.uuid) + if leecher != nil { + syncFile.m.Unlock() + leecher.InitDownload(conn) + syncFile.m.Lock() + } else { + return fmt.Errorf("leecher not found when we are downloading") + } + + } + return nil + } +} + +func (syncFile *SyncFileImpl) SaveFileInfo() error { + syncFile.m.Lock() + defer syncFile.m.Unlock() + infoMap := syncFile.CopyFile.GetFileInfoMap() + infoMap["uuid"] = syncFile.uuid + fileInfo, err := json.Marshal(infoMap) + if err != nil { + return err + } + err = os.WriteFile(filepath.Join(syncFile.baseFilesDir, PATH_COPY_FILES, ".info", syncFile.uuid+".info"), fileInfo, 0644) + if err != nil { + log.Println("Error while saving file info: ", err) + return err + } + return nil +} + +func LoadSyncFileInfo(baseDir string, path string) (*SyncFileImpl, error) { + openedFile, err := os.Open(path) + if err != nil { + return nil, err + } + decoder := json.NewDecoder(openedFile) + fileInfoMap := make(map[string]interface{}) + err = decoder.Decode(&fileInfoMap) + openedFile.Close() + if err != nil { + return nil, err + } + syncFile := NewSyncFile(baseDir) + syncFile.uuid = fileInfoMap["uuid"].(string) + CopyFile, err := LoadFileInfo(fileInfoMap) + if err != nil { + return nil, err + } + syncFile.CopyFile = CopyFile + + realFile, err := LoadFile(baseDir, CopyToRealPath(CopyFile.GetRelativePath()), CopyFile.GetName()) + if err != nil { + return nil, err + } + syncFile.RealFile = realFile + + newHash, err := CopyFile.CalculateHash() + if err != nil { + return nil, err + } + if newHash != CopyFile.hash { + // the file in the Copy has probably been modified + // it occurs when the node is closed while a file is being downloaded + CopyFile.version = 0 + return nil, err + } + CopyFile.UpdateMetadata() + return syncFile, nil +} + +// Getters and Setters + +func (syncFile *SyncFileImpl) GetRealFile() File { + return syncFile.RealFile +} + +func (syncFile *SyncFileImpl) GetCopyFile() File { + return syncFile.CopyFile +} + +func (syncFile *SyncFileImpl) GetUUID() string { + syncFile.m.RLock() + defer syncFile.m.RUnlock() + return syncFile.uuid +} + +func (syncFile *SyncFileImpl) SetUUID(uuid string) { + syncFile.m.Lock() + defer syncFile.m.Unlock() + syncFile.uuid = uuid +} + +func (syncFile *SyncFileImpl) SetFileTransfer(ft FileTransfer) { + syncFile.m.Lock() + defer syncFile.m.Unlock() + syncFile.FileTransfer = ft +} + +func (syncFile *SyncFileImpl) GetLastOperation() Operation { + syncFile.m.RLock() + defer syncFile.m.RUnlock() + return syncFile.lastOperation +} + +func (syncFile *SyncFileImpl) getShortInfoMsg() msg.FileMessage { + info := msg.FileMessage{MessageName: "sendInfo", + FileUUID: syncFile.uuid, + FileName: syncFile.CopyFile.GetName(), + FileHash: syncFile.CopyFile.GetHash(), + FileVersion: syncFile.CopyFile.GetVersion(), + FilePath: syncFile.RealFile.GetRelativePath(), + FileOperation: syncFile.lastOperation.OperationMessage(), + } + info.InitMessageBase() + return info +} + +func (syncFile *SyncFileImpl) GetShortInfoMsg() msg.FileMessage { + syncFile.m.RLock() + defer syncFile.m.RUnlock() + return syncFile.getShortInfoMsg() +} + +// like shortInfo but we add the hashmap +func (syncFile *SyncFileImpl) GetFullInfoMsg() msg.FileMessage { + syncFile.m.RLock() + defer syncFile.m.RUnlock() + info := syncFile.getShortInfoMsg() + info.FileHashMap = syncFile.CopyFile.GetHashMap() + info.FileSize = syncFile.CopyFile.GetSize() + return info +} + +func (syncFile *SyncFileImpl) GetFileState() FileState { + syncFile.m.RLock() + defer syncFile.m.RUnlock() + return FileState{ + UUID: syncFile.uuid, + Name: syncFile.CopyFile.GetName(), + Hash: syncFile.CopyFile.GetHash(), + Version: syncFile.CopyFile.GetVersion(), + Path: syncFile.RealFile.GetRelativePath(), + LastOperation: syncFile.lastOperation, + } +} + +func (syncFile *SyncFileImpl) SetStatus(status string) { + syncFile.m.Lock() + defer syncFile.m.Unlock() + syncFile.status = status +} + +func (syncFile *SyncFileImpl) GetStatus() string { + syncFile.m.RLock() + defer syncFile.m.RUnlock() + return syncFile.status +} diff --git a/file/utils.go b/file/utils.go new file mode 100644 index 0000000..55d8ba9 --- /dev/null +++ b/file/utils.go @@ -0,0 +1,131 @@ +package fileSync + +import ( + "crypto/sha256" + "encoding/hex" + "math" + "os" + "path/filepath" + "strings" + "syscall" + + "github.com/ditrit/shoset/msg" + "github.com/rs/zerolog" +) + +type ShosetConn interface { + SendMessage(msg.Message) error + GetLocalAddress() string + GetRemoteAddress() string + GetTCPInfo() (*syscall.TCPInfo, error) + GetLogger() *zerolog.Logger +} + +// contains range through a slice to search for a particular string +func Contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + return false +} + +func Max64(x int64, y int64) int64 { + if x < y { + return y + } + return x +} + +func Min64(x int64, y int64) int64 { + if x > y { + return y + } + return x +} + +func Max(x int, y int) int { + if x < y { + return y + } + return x +} + +func Min(x int, y int) int { + if x > y { + return y + } + return x +} + +func CalculateNumberOfChunks(fileSize int64, chunkSize int) int { + return int(math.Ceil((float64(fileSize) / float64(chunkSize)))) +} + +// Give the optimal size of a piece of a file to have a better transfer rate +// The given size is between 256Kbytes and 1Mbytes (and given in bytes) +// The greater the file size, the greater the piece size +func CalculatePieceSize(fileSize int64) int { + opt := int(fileSize / 1000) + if opt <= 256*1024 { + return 256 * 1024 + } + if opt >= 1024*1024 { + return 1024 * 1024 + } + return 512 * 1024 +} + +func Hash(data []byte) string { + hash := sha256.Sum256(data) + return hex.EncodeToString(hash[:]) +} + +// convert a relative path from real to Copy (add .Copy before) +func RealToCopyPath(path string) string { + listFolder := strings.Split(path, string(os.PathSeparator)) + listFolder = append([]string{PATH_COPY_FILES}, listFolder...) + return filepath.Join(listFolder...) +} + +// convert a relative path from real to Copy (add .Copy before) +func CopyToRealPath(path string) string { + listFolder := strings.Split(path, string(os.PathSeparator)) + return filepath.Join(listFolder[1:]...) +} + +// get the relative path from a full path, assuming baseDir is in the path +func PathToRelativePath(path string, baseDir string) string { + listFolder := strings.Split(path, string(os.PathSeparator)) + listBaseDir := strings.Split(baseDir, string(os.PathSeparator)) + newList := listFolder[len(listBaseDir):] + return filepath.Join(newList...) +} + +// tells if the file/folder is in a subdirectory by comparing paths +func IsInDir(path string, dir string) bool { + + listFolder := strings.Split(path, string(os.PathSeparator)) + listDir := strings.Split(dir, string(os.PathSeparator)) + + if len(listFolder) < len(listDir) { + return false + } + for i := 0; i < len(listDir); i++ { + if listFolder[i] != listDir[i] { + return false + } + } + return true +} + +// get directory from a path +// like filepath.Dir but return "" instead of "." when no directory +func Dir(path string) string { + dir := filepath.Dir(path) + if dir == "." { + return "" + } + return dir +} diff --git a/go.mod b/go.mod index faa8c8d..56a33d4 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,12 @@ module github.com/ditrit/shoset go 1.14 require ( + github.com/golang/mock v1.6.0 + github.com/google/uuid v1.1.2 + github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c github.com/kjk/betterguid v0.0.0-20170621091430-c442874ba63a + github.com/rs/zerolog v1.26.1 github.com/spf13/viper v1.8.0 + github.com/square/certstrap v1.2.0 + github.com/urfave/cli v1.21.0 ) diff --git a/go.sum b/go.sum index 07e7e60..ab36047 100644 --- a/go.sum +++ b/go.sum @@ -56,6 +56,7 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -85,6 +86,8 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -131,9 +134,11 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -157,19 +162,25 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/howeyc/gopass v0.0.0-20170109162249-bf9dde6d0d2c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= +github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c h1:aY2hhxLhjEAbfXOx2nRJxCXezC6CO2V/yN+OCr1srtk= +github.com/howeyc/gopass v0.0.0-20190910152052-7cb4b85ec19c/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kjk/betterguid v0.0.0-20170621091430-c442874ba63a h1:b+Gt8sQs//Sl5Dcem5zP9Qc2FgEUAygREa2AAa2Vmcw= github.com/kjk/betterguid v0.0.0-20170621091430-c442874ba63a/go.mod h1:uxRAhHE1nl34DpWgfe0CYbNYbCnYplaB6rZH9ReWtUk= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -192,15 +203,22 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5dSQ= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc= +github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= @@ -212,20 +230,26 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.8.0 h1:QRwDgoG8xX+kp69di68D+YYTCWfYEckbZRfUlEIAal0= github.com/spf13/viper v1.8.0/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/square/certstrap v1.2.0 h1:ecgyABrbFLr8jSbOC6oTBmBek0t/HqtgrMUZCPuyfdw= +github.com/square/certstrap v1.2.0/go.mod h1:CUHqV+fxJW0Y5UQFnnbYwQ7bpKXO1AKbic9g73799yw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/urfave/cli v1.21.0 h1:wYSSj06510qPIzGSua9ZqsncMmWE3Zr55KBERygyrxE= +github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= @@ -240,12 +264,15 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e h1:1SzTfNOXwIS2oWiMF+6qu0OUDKb0dauo6MoDUQyu+yU= +golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -317,6 +344,7 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -343,6 +371,7 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -380,8 +409,11 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -389,8 +421,9 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -444,7 +477,9 @@ golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -552,6 +587,7 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= @@ -562,6 +598,7 @@ gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/handler_command.go b/handler_command.go new file mode 100644 index 0000000..6ff740d --- /dev/null +++ b/handler_command.go @@ -0,0 +1,107 @@ +package shoset + +import ( + "time" + + "github.com/ditrit/shoset/msg" +) + +// CommandHandler implements MessageHandlers interface. +type CommandHandler struct{} + +// Get returns the message for a given ShosetConn. +func (ch *CommandHandler) Get(c *ShosetConn) (msg.Message, error) { + var m msg.Command + err := c.GetReader().ReadMessage(&m) + return m, err +} + +// HandleDoubleWay handles message for a ShosetConn accordingly. +func (ch *CommandHandler) HandleDoubleWay(c *ShosetConn, message msg.Message) error { + m := message.(msg.Command) + // log the fact that we received a command + c.Logger.Info().Msg("received command : " + m.GetPayload()) + if notInQueue := c.GetShoset().Queue["cmd"].Push(m, c.GetRemoteShosetType(), c.GetLocalAddress()); notInQueue { + ch.Send(c.GetShoset(), m) + c.GetShoset().MessageEventBus.Publish("cmd", true) // Notifies of the reception of a new message + } + + c.GetShoset().MessageEventBus.Publish("cmd", true) // Notifies of the reception of a new message + + return nil +} + +// Send sends the message through the given Shoset network. (through all the connections) +func (ch *CommandHandler) Send(s *Shoset, m msg.Message) { + _ = s.Queue["cmd"].Push(m, VOID, VOID) + // get the target of the message m + var target = m.(msg.Command).GetTarget() + s.ConnsByLname.Iterate( + func(lname string, ipAddress string, conn interface{}) { + if lname == target { + s.Logger.Debug().Msg("sending command to " + ipAddress) + if err := conn.(*ShosetConn).GetWriter().SendMessage(m); err != nil { + conn.(*ShosetConn).Logger.Warn().Msg("couldn't send command msg : " + err.Error()) + } + } + }, + ) +} + +// Wait returns the message received for a given Shoset. +func (ch *CommandHandler) Wait(s *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { + timer := time.NewTimer(time.Duration(timeout) * time.Second) + + commandName, ok := args["name"] + if !ok { + s.Logger.Error().Msg("no command name provided for Wait") + return nil + } + + // Checks every message in the queue before waiting for a new message + // Checks message presence in two steps to avoid accessing attributs of + for { + cell := replies.Get() + if cell != nil { + message := cell.GetMessage() + if message != nil { + cmd := message.(msg.Command) + if cmd.GetCommand() == commandName { + return &message + } + } + } else { + // Locking Queue to avoid missing a message while preparing the channel to receive events. + replies.GetQueue().LockQueue() + break + } + } + // Creating channel + chNewMessage := make(chan interface{}) + s.MessageEventBus.Subscribe("cmd", chNewMessage) + replies.GetQueue().UnlockQueue() + defer s.MessageEventBus.UnSubscribe("cmd", chNewMessage) + + for { + select { + case <-timer.C: + s.Logger.Warn().Msg("No message received in Wait Command (timeout).") + return nil + case <-chNewMessage: + //Checks message presence in two steps to avoid accessing fields of + s.Logger.Debug().Msg("New message received in Wait Command.") + cell := replies.Get() + if cell == nil { + break + } + message := cell.GetMessage() + if message == nil { + break + } + cmd := message.(msg.Command) + if cmd.GetCommand() == commandName { + return &message + } + } + } +} diff --git a/handler_config.go b/handler_config.go new file mode 100644 index 0000000..256ae1c --- /dev/null +++ b/handler_config.go @@ -0,0 +1,69 @@ +package shoset + +import ( + "errors" + "time" + + "github.com/ditrit/shoset/msg" +) + +// ConfigHandler implements MessageHandlers interface. +type ConfigHandler struct{} + +// MOVE TO GANDALF +// Get returns the message for a given ShosetConn. +func (ch *ConfigHandler) Get(c *ShosetConn) (msg.Message, error) { + var conf msg.Config + err := c.GetReader().ReadMessage(&conf) + return conf, err +} + +// HandleDoubleWay handles message for a ShosetConn accordingly. +func (ch *ConfigHandler) HandleDoubleWay(c *ShosetConn, message msg.Message) error { + conf := message.(msg.Config) + if notInQueue := c.GetShoset().Queue["cmd"].Push(conf, c.GetRemoteShosetType(), c.GetLocalAddress()); !notInQueue { + return errors.New("failed to handle command") + } + return nil +} + +// Send sends the message through the given Shoset network. +func (ch *ConfigHandler) Send(s *Shoset, cmd msg.Message) { + s.ConnsByLname.Iterate( + func(lname string, ipAddress string, conn interface{}) { + if err := conn.(*ShosetConn).GetWriter().SendMessage(cmd); err != nil { + conn.(*ShosetConn).Logger.Warn().Msg("couldn't send config msg : " + err.Error()) + } + }, + ) +} + +// Wait returns the message received for a given Shoset. +func (ch *ConfigHandler) Wait(s *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { + commandName, ok := args["name"] + if !ok { + return nil + } + term := make(chan *msg.Message, 1) + cont := true + go func() { + for cont { + message := replies.Get().GetMessage() + if message == nil { + time.Sleep(time.Duration(10) * time.Millisecond) + continue + } + config := message.(msg.Config) + if config.GetCommand() == commandName { + term <- &message + } + } + }() + select { + case res := <-term: + cont = false + return res + case <-time.After(time.Duration(timeout) * time.Second): + return nil + } +} diff --git a/handler_config_bye.go b/handler_config_bye.go new file mode 100644 index 0000000..88ad37e --- /dev/null +++ b/handler_config_bye.go @@ -0,0 +1,60 @@ +package shoset + +import ( + "github.com/ditrit/shoset/msg" + "github.com/rs/zerolog/log" +) + +// ConfigByeHandler implements MessageHandlers interface. +type ConfigByeHandler struct{} + +// Get returns the message for a given ShosetConn. +func (cbh *ConfigByeHandler) Get(c *ShosetConn) (msg.Message, error) { + var cfg msg.ConfigProtocol + err := c.GetReader().ReadMessage(&cfg) + return cfg, err + +} + +// HandleDoubleWay handles message for a ShosetConn accordingly. +func (cbh *ConfigByeHandler) HandleDoubleWay(c *ShosetConn, message msg.Message) error { + + cfg := message.(msg.ConfigProtocol) + + switch cfg.GetCommandName() { + case PROTOCOL_EXIT: + // incoming bye request. + // send delete signal to all connected shosets from our list of known shosets. + cfgNewDelete := msg.NewConfigProtocol(cfg.GetAddress(), c.GetShoset().GetLogicalName(), c.GetShoset().GetShosetType(), DELETE) + c.GetShoset().ConnsByLname.Iterate( + func(lname string, ipAddress string, bro interface{}) { // Envoyé à tous, pas seulement les brothers ? + if ipAddress != cfg.GetAddress() { + err := bro.(*ShosetConn).GetWriter().SendMessage(*cfgNewDelete) + if err != nil { + bro.(*ShosetConn).Logger.Warn().Msg("couldn't send cfgNewDelete : " + err.Error()) + } + } + }, + ) + + case DELETE: + // incoming delete signal. + // forget the concerned shoset from our list of known shosets and close connection. + + c.GetShoset().DeleteConn(cfg.GetLogicalName(), cfg.GetAddress()) + } + return nil +} + +// Send sends the message through the given Shoset network. +func (cbh *ConfigByeHandler) Send(s *Shoset, m msg.Message) { + // no-op + log.Warn().Msg("ConfigByeHandler.Send not implemented") +} + +// Wait returns the message received for a given Shoset. +func (cbh *ConfigByeHandler) Wait(s *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { + // no-op + log.Warn().Msg("ConfigByeHandler.Wait not implemented") + return nil +} diff --git a/handler_config_join.go b/handler_config_join.go new file mode 100644 index 0000000..9c87a43 --- /dev/null +++ b/handler_config_join.go @@ -0,0 +1,121 @@ +package shoset + +import ( + "sync" + + "github.com/ditrit/shoset/msg" + "github.com/rs/zerolog/log" +) + +// ConfigJoinHandler implements MessageHandlers interface. +type ConfigJoinHandler struct{} + +// Get returns the message for a given ShosetConn. +func (cjh *ConfigJoinHandler) Get(c *ShosetConn) (msg.Message, error) { + var cfg msg.ConfigProtocol + err := c.GetReader().ReadMessage(&cfg) + return cfg, err +} + +// HandleDoubleWay handles message for a ShosetConn accordingly. +func (cjh *ConfigJoinHandler) HandleDoubleWay(c *ShosetConn, message msg.Message) error { + cfg := message.(msg.ConfigProtocol) + + switch cfg.GetCommandName() { + case PROTOCOL_JOIN: + // incoming join request, a socket wants to join to this one. + // save info and retrieve brothers to inform network. + + c.Logger.Trace().Str("lname", cfg.GetLogicalName()).Str("IP", cfg.GetAddress()).Msg("Incoming join request : " + PROTOCOL_JOIN) + + c.GetShoset().DeleteConn(cfg.GetLogicalName(), cfg.GetAddress()) + + c.SetRemoteAddress(cfg.GetAddress()) + c.Store(PROTOCOL_JOIN, c.GetShoset().GetLogicalName(), cfg.GetAddress(), c.GetShoset().GetShosetType()) + + // Send ACKNOWLEDGE_JOIN + configOk := msg.NewConfigProtocol(cfg.GetAddress(), c.GetShoset().GetLogicalName(), c.GetShoset().GetShosetType(), ACKNOWLEDGE_JOIN) + if err := c.GetWriter().SendMessage(*configOk); err != nil { + c.Logger.Warn().Msg("couldn't send configOk : " + err.Error()) + } + + // Notify all the "members" = localBrothers (the ones with the same Lname) that a new member has joined so that they can all initiate a connection with the new member. + cfgNewMember := msg.NewConfigProtocol(cfg.GetAddress(), c.GetShoset().GetLogicalName(), c.GetShoset().GetShosetType(), MEMBER) + mapConns, _ := c.GetShoset().ConnsByLname.Load(c.GetShoset().GetLogicalName()) + mapConns.(*sync.Map).Range(func(key, value interface{}) bool { + func(address string, bro interface{}) { + if address == cfg.GetAddress() { + return + } + if err := bro.(*ShosetConn).GetWriter().SendMessage(*cfgNewMember); err != nil { + bro.(*ShosetConn).Logger.Warn().Msg("couldn't send cfgNewMember : " + err.Error()) + } + }(key.(string), value) + return true + }) + + // Notify all the remoteBrothers (the ones with a different Lname) that a new member has joined so that they can all initiate a connection with the new member. + localBrothersArray := []string{} // localBrothers = members = socket with the same Lname + if localBrothers, _ := c.GetShoset().ConnsByLname.Load(c.GetShoset().GetLogicalName()); localBrothers != nil { + localBrothersArray = Keys(localBrothers.(*sync.Map), ALL) + } + + c.GetShoset().ConnsByLname.Iterate( + func(lname string, ipAddress string, remoteBro interface{}) { + if lname != c.GetShoset().GetLogicalName() { // if remoteBrother + c.Logger.Debug().Msg("sending brother info to " + ipAddress) + remoteBrothers, _ := c.GetShoset().ConnsByLname.Load(lname) // remoteBrothers = sockets with the remote Lname = sockets linked to this one + remoteBrothersArray := []string{} + if remoteBrothers != nil { + remoteBrothersArray = Keys(remoteBrothers.(*sync.Map), ALL) // get the IP addresses of the remoteBrothers + } + cfgBrothers := msg.NewConfigBrothersProtocol(localBrothersArray, remoteBrothersArray, BROTHERS, c.GetShoset().GetLogicalName(), c.GetShoset().GetShosetType()) + if err := remoteBro.(*ShosetConn).GetWriter().SendMessage(*cfgBrothers); err != nil { + remoteBro.(*ShosetConn).Logger.Warn().Msg("couldn't send brothers : " + err.Error()) + } + } + }, + ) + + case ACKNOWLEDGE_JOIN: + // incoming acknowledge_join, join request validated. + // save info. + + c.Logger.Trace().Str("lname", cfg.GetLogicalName()).Str("IP", cfg.GetAddress()).Msg("Incoming acknowledge join : " + ACKNOWLEDGE_JOIN) + + // c.GetShoset().DeleteConn(cfg.GetLogicalName(), cfg.GetAddress()) // we never delete a conn + + c.Store(PROTOCOL_JOIN, c.GetShoset().GetLogicalName(), c.GetRemoteAddress(), c.GetShoset().GetShosetType()) + + // Deletes the IP from the list of started but not yet ready. + c.GetShoset().LaunchedProtocol.DeleteFromConcurentSlice(c.GetRemoteAddress()) + + case MEMBER: + // incoming member information. + // need to link protocol on it if not already in the map of known conn. + + c.Logger.Trace().Str("lname", cfg.GetLogicalName()).Str("IP", cfg.GetAddress()).Msg("Incoming brother member : " + MEMBER) + + mapConns, _ := c.GetShoset().ConnsByLname.Load(c.GetShoset().GetLogicalName()) + if mapConns == nil { + return nil + } + if exists, _ := mapConns.(*sync.Map).Load(cfg.GetAddress()); exists == nil { + c.GetShoset().Protocol(c.GetShoset().GetBindAddress(), cfg.GetAddress(), PROTOCOL_JOIN) + } + } + return nil +} + +// Send sends the message through the given Shoset network. +func (cjh *ConfigJoinHandler) Send(s *Shoset, m msg.Message) { + // no-op + log.Warn().Msg("ConfigJoinHandler.Send not implemented") +} + +// Wait returns the message received for a given Shoset. +func (cjh *ConfigJoinHandler) Wait(s *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { + // no-op + log.Warn().Msg("ConfigJoinHandler.Wait not implemented") + return nil +} diff --git a/handler_config_join_test.go b/handler_config_join_test.go new file mode 100644 index 0000000..9f67660 --- /dev/null +++ b/handler_config_join_test.go @@ -0,0 +1,55 @@ +package shoset + +import ( + "crypto/tls" + "testing" + "time" + + "github.com/ditrit/shoset/msg" +) + +const ( + s9001 = "127.0.0.1:9001" + s9002 = "127.0.0.1:9002" +) + +func TestHandleDoubleWay(t *testing.T) { + shoset.InitPKI(s9001) + + shoset2 := NewShoset("cl", "cl") + shoset2.ConnsByLname.GetConfig().SetFileName("127-0-0-1_9002") + _, err = shoset2.ConnsByLname.GetConfig().InitFolders("127-0-0-1_9002") + if err != nil { + shoset2.Logger.Error().Msg("couldn't init folder: " + err.Error()) + return + } + err = shoset2.Certify(s9002, s9001) + if err != nil { + shoset2.Logger.Error().Msg("couldn't certify: " + err.Error()) + return + } + shoset2.Bind(s9002) + + handler, ok := shoset.Handlers["cfgjoin"] + if !ok { + t.Errorf("HandleDoubleWay didn't work, handler not ok") + } + + c, _ := NewShosetConn(shoset2, s9001, OUT) + + protocolConn, err := tls.Dial(CONNECTION_TYPE, c.GetRemoteAddress(), c.GetShoset().GetTlsConfigDoubleWay()) + if err != nil { + time.Sleep(time.Millisecond * time.Duration(5000)) + c.Logger.Error().Msg("HandleConfig err: " + err.Error()) + } + defer func() { + c.Logger.Debug().Msg("HandleConfig: socket closed") + c.GetConn().Close() + }() + c.UpdateConn(protocolConn) + + cfg := msg.NewConfigProtocol(s9002, shoset2.GetLogicalName(), shoset2.GetShosetType(), PROTOCOL_JOIN) + go handler.HandleDoubleWay(c, *cfg) + + time.Sleep(time.Millisecond * time.Duration(1000)) +} diff --git a/handler_config_link.go b/handler_config_link.go new file mode 100644 index 0000000..bee026a --- /dev/null +++ b/handler_config_link.go @@ -0,0 +1,143 @@ +package shoset + +import ( + "sync" + + "github.com/ditrit/shoset/msg" + "github.com/rs/zerolog/log" +) + +// ConfigLinkHandler implements MessageHandlers interface. +type ConfigLinkHandler struct{} + +// Get returns the message for a given ShosetConn. +func (clh *ConfigLinkHandler) Get(c *ShosetConn) (msg.Message, error) { + var cfg msg.ConfigProtocol + err := c.GetReader().ReadMessage(&cfg) + return cfg, err +} + +// HandleDoubleWay handles message for a ShosetConn accordingly. +func (clh *ConfigLinkHandler) HandleDoubleWay(c *ShosetConn, message msg.Message) error { + cfg := message.(msg.ConfigProtocol) + + switch cfg.GetCommandName() { + case PROTOCOL_LINK: + // incoming link request, a socket wants to link to this one. + // save info and retrieve brothers to inform network. + + c.Logger.Trace().Str("lname", cfg.GetLogicalName()).Str("IP", cfg.GetAddress()).Msg("Incoming link request : " + PROTOCOL_LINK) + + c.GetShoset().DeleteConn(cfg.GetLogicalName(), cfg.GetAddress()) + + c.SetRemoteAddress(cfg.GetAddress()) + c.Store(PROTOCOL_LINK, cfg.GetLogicalName(), cfg.GetAddress(), cfg.GetShosetType()) + + // Send ACKNOWLEDGE_LINK + configOk := msg.NewConfigProtocol(cfg.GetAddress(), c.GetShoset().GetLogicalName(), c.GetShoset().GetShosetType(), ACKNOWLEDGE_LINK) + if err := c.GetWriter().SendMessage(*configOk); err != nil { + c.Logger.Warn().Msg("couldn't send configOk : " + err.Error()) + } + + localBrothersArray := []string{} // localBrothers = members = socket with the same Lname + if localBrothers, _ := c.GetShoset().ConnsByLname.Load(c.GetShoset().GetLogicalName()); localBrothers != nil { + localBrothersArray = Keys(localBrothers.(*sync.Map), ALL) + } + + remoteBrothers, _ := c.GetShoset().ConnsByLname.Load(c.GetRemoteLogicalName()) // brothers = sockets with the remote Lname = sockets linked to this one + remoteBrothersArray := []string{} + if remoteBrothers != nil { + remoteBrothersArray = Keys(remoteBrothers.(*sync.Map), ALL) + } + + // we send the list of localBrothers to the remoteBrothers + cfgBrothers := msg.NewConfigBrothersProtocol(localBrothersArray, remoteBrothersArray, BROTHERS, c.GetShoset().GetLogicalName(), c.GetShoset().GetShosetType()) + remoteBrothers.(*sync.Map).Range(func(_, value interface{}) bool { + func(_, remoteBro interface{}) { + if err := remoteBro.(*ShosetConn).GetWriter().SendMessage(*cfgBrothers); err != nil { + remoteBro.(*ShosetConn).Logger.Warn().Msg("couldn't send brothers : " + err.Error()) + } + }(nil, value) + return true + }) + + case ACKNOWLEDGE_LINK: + // incoming acknowledge_link, link request accepted. + + c.Logger.Trace().Str("lname", cfg.GetLogicalName()).Str("IP", cfg.GetAddress()).Msg("Incoming acknowledge link : " + ACKNOWLEDGE_LINK) + + c.GetShoset().DeleteConn(cfg.GetLogicalName(), cfg.GetAddress()) + + c.Store(PROTOCOL_LINK, cfg.GetLogicalName(), c.GetRemoteAddress(), cfg.GetShosetType()) + + // Deletes the IP from the list of started but not yet ready. + c.GetShoset().LaunchedProtocol.DeleteFromConcurentSlice(c.GetRemoteAddress()) + + case BROTHERS: + // incoming brother information, new shoset in the network. + // save info and call sendToBrothers to handle message. + + c.Logger.Trace().Str("lname", cfg.GetLogicalName()).Str("IP", cfg.GetAddress()).Msg("Incoming brother information : " + BROTHERS) + + c.Store(PROTOCOL_LINK, cfg.GetLogicalName(), c.GetRemoteAddress(), cfg.GetShosetType()) + + sendToBrothers(c, message) + } + return nil +} + +// sendToBrothers : handle link brothers. +// retrieve info concerning local and remote brothers and handle them. +func sendToBrothers(c *ShosetConn, m msg.Message) { + cfg := m.(msg.ConfigProtocol) + localBrothers := cfg.GetYourBrothers() + remoteBrothers := cfg.GetMyBrothers() + + // handle local brothers (same type of shoset). + // need to add them to our known shosets as "fake" connection but do not protocol on it. + for _, bro := range localBrothers { + if bro == c.GetShoset().GetBindAddress() { // do not add myself + continue + } + + if _, ok := c.GetShoset().ConnsByLname.Load(bro); ok { // if bro is in ConnsByLname, it means it is already known + continue + } + + newConn, _ := NewShosetConn(c.GetShoset(), bro, ME) // create a fake connection with no direction + newConn.SetRemoteLogicalName(c.GetLocalLogicalName()) + newConn.SetRemoteShosetType(c.GetLocalShosetType()) + + mapSync := new(sync.Map) + mapSync.Store(c.GetLocalLogicalName(), true) + c.GetShoset().LnamesByProtocol.Store(PROTOCOL_LINK, mapSync) + c.GetShoset().LnamesByType.Store(c.GetRemoteShosetType(), mapSync) + c.GetShoset().ConnsByLname.StoreConfig(c.GetShoset().GetLogicalName(), bro, PROTOCOL_LINK, newConn) + + } + + // handle remote brothers (not the same type of shoset). + // need to link protocol on them if not already in the map of known conn. + mapConns, _ := c.GetShoset().ConnsByLname.Load(cfg.GetLogicalName()) + if mapConns == nil { + return + } + for _, remoteBrother := range remoteBrothers { + if exists, _ := mapConns.(*sync.Map).Load(remoteBrother); exists == nil { + c.GetShoset().Protocol(c.GetShoset().GetBindAddress(), remoteBrother, PROTOCOL_LINK) + } + } +} + +// Send sends the message through the given Shoset network. +func (clh *ConfigLinkHandler) Send(s *Shoset, m msg.Message) { + // no-op + log.Warn().Msg("ConfigLinkHandler.Send not implemented") +} + +// Wait returns the message received for a given Shoset. +func (clh *ConfigLinkHandler) Wait(s *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { + // no-op + log.Warn().Msg("ConfigLinkHandler.Wait not implemented") + return nil +} diff --git a/handler_event.go b/handler_event.go new file mode 100644 index 0000000..aeaceda --- /dev/null +++ b/handler_event.go @@ -0,0 +1,102 @@ +package shoset + +import ( + "time" + + "github.com/ditrit/shoset/msg" +) + +// EventHandler implements MessageHandlers interface. +type EventHandler struct{} + +// Get returns the message for a given ShosetConn. +func (eh *EventHandler) Get(c *ShosetConn) (msg.Message, error) { + var evt msg.Event + err := c.GetReader().ReadMessage(&evt) + return evt, err +} + +// HandleDoubleWay handles message for a ShosetConn accordingly. +func (eh *EventHandler) HandleDoubleWay(c *ShosetConn, message msg.Message) error { + evt := message.(msg.Event) + if notInQueue := c.GetShoset().Queue["evt"].Push(evt, c.GetRemoteShosetType(), c.GetLocalAddress()); notInQueue { + eh.Send(c.GetShoset(), evt) + } + + c.GetShoset().MessageEventBus.Publish("evt", true) // Notifies of the reception of a new message + + return nil +} + +// Send sends the message through the given Shoset network. +func (eh *EventHandler) Send(s *Shoset, evt msg.Message) { + _ = s.Queue["evt"].Push(evt, VOID, VOID) + s.ConnsByLname.Iterate( + func(lname string, ipAddress string, conn interface{}) { + err := conn.(*ShosetConn).GetWriter().SendMessage(evt) + if err != nil { + s.Logger.Warn().Msg(s.GetLogicalName() + " couldn't send evt to " + conn.(*ShosetConn).GetRemoteLogicalName() + ": " + err.Error()) + } else { + s.Logger.Debug().Msg("evt sent successfully from " + s.GetLogicalName() + " to " + conn.(*ShosetConn).GetRemoteLogicalName()) + } + }, + ) +} + +// Wait returns the message received for a given Shoset. +func (eh *EventHandler) Wait(s *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { + timer := time.NewTimer(time.Duration(timeout) * time.Second) + + topicName, ok := args["topic"] + if !ok { + s.Logger.Error().Msg("no topic provided for Wait evt") + return nil + } + + // Checks every message in the queue before waiting for a new message + // Checks message presence in two steps to avoid accessing attributs of + for { + cell := replies.Get() + if cell != nil { + message := cell.GetMessage() + if message != nil { + event := message.(msg.Event) + if event.GetTopic() == topicName && (args["event"] == VOID || event.GetEvent() == args["event"]) { + return &message + } + } + } else { + // Locking Queue to avoid missing a message while preparing the channel to receive events. + replies.GetQueue().LockQueue() + break + } + } + // Creating channel + chNewMessage := make(chan interface{}) + s.MessageEventBus.Subscribe("evt", chNewMessage) + replies.GetQueue().UnlockQueue() + defer s.MessageEventBus.UnSubscribe("evt", chNewMessage) + + for { + select { + case <-timer.C: + s.Logger.Warn().Msg("No message received in Wait event (timeout).") + return nil + case <-chNewMessage: + //Checks message presence in two steps to avoid accessing fields of + s.Logger.Debug().Msg("New message received in Wait event.") + cell := replies.Get() + if cell == nil { + break + } + message := cell.GetMessage() + if message == nil { + break + } + event := message.(msg.Event) + if event.GetTopic() == topicName && (args["event"] == VOID || event.GetEvent() == args["event"]) { + return &message + } + } + } +} diff --git a/handler_fileTransfer.go b/handler_fileTransfer.go new file mode 100644 index 0000000..6faca54 --- /dev/null +++ b/handler_fileTransfer.go @@ -0,0 +1,115 @@ +package shoset + +import ( + "errors" + "fmt" + "time" + + fileMod "github.com/ditrit/shoset/file" + "github.com/ditrit/shoset/msg" +) + +// EventHandler implements MessageHandlers interface. +type FileHandler struct { + fileMod.FileTransferImpl +} + +// Get returns the message for a given ShosetConn. +func (fh *FileHandler) Get(c *ShosetConn) (msg.Message, error) { + var fm msg.FileMessage + err := c.GetReader().ReadMessage(&fm) + if err != nil { + return nil, err + } + if contains([]string{"authorised", "unauthorised", "congestion", "askInfo", "interested", "notInterested", "have", "askBitfield", "sendBitfield", "sendInfo", "sendChunk", "askChunk", "sendLibrary", "sendFileOperation", "askLibraryLocked", "answerLibraryLocked"}, fm.MessageName) { + return fm, err + } + return fm, errors.New("message not found : " + fm.MessageName) +} + +// HandleDoubleWay handles message for a ShosetConn accordingly. +func (fh *FileHandler) HandleDoubleWay(c *ShosetConn, message msg.Message) error { + if fh.GetLibrary() == nil { // the library has not been initialised + c.GetShoset().Logger.Warn().Msg("library not initialised") + return nil + } + fileMessage := message.(msg.FileMessage) + switch fileMessage.MessageName { + case "sendChunk": + c.Logger.Debug().Msg(c.GetLocalAddress() + " received " + fileMessage.MessageName + " from " + c.GetRemoteAddress() + " /" + " begin: " + fmt.Sprint(fileMessage.Begin) + " length: " + fmt.Sprint(fileMessage.Length)) + case "askChunk": + c.Logger.Debug().Msg(c.GetLocalAddress() + " received " + fileMessage.MessageName + " from " + c.GetRemoteAddress() + " /" + " begin: " + fmt.Sprint(fileMessage.Begin) + " length: " + fmt.Sprint(fileMessage.Length)) + case "sendBitfield": + c.Logger.Debug().Msg(c.GetLocalAddress() + " received " + fileMessage.MessageName + " from " + c.GetRemoteAddress() + " /" + " with " + fmt.Sprint(fileMessage.Bitfield)) + case "askBitfield": + c.Logger.Debug().Msg(c.GetLocalAddress() + " received " + fileMessage.MessageName + " from " + c.GetRemoteAddress()) + + default: + c.Logger.Debug().Msg(c.GetLocalAddress() + " received " + fileMessage.MessageName + " from " + c.GetRemoteAddress()) + } + fh.ReceiveMessage(&fileMessage, c) + return nil +} + +// Send a message +func (fh *FileHandler) Send(s *Shoset, message msg.Message) { + s.ConnsByLname.Iterate( + func(lname string, ipAddress string, conn interface{}) { + if lname == s.GetLogicalName() { // if same Lname as the me + conn.(*ShosetConn).GetWriter().SendMessage(message) + } + }, + ) +} + +// Wait returns the message received for a given Shoset. +func (fh *FileHandler) Wait(s *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { + timer := time.NewTimer(time.Duration(timeout) * time.Second) + defer timer.Stop() + // Checks every message in the queue before waiting for a new message + // Checks message presence in two steps to avoid accessing attributs of + for { + cell := replies.Get() + if cell != nil { + message := cell.GetMessage() + if message != nil { + fileMsg := message.(msg.FileMessage) + if args["fileName"] == VOID || fileMsg.FileName == args["fileName"] { + return &message + } + } + } else { + // Locking Queue to avoid missing a message while preparing the channel to receive events. + replies.GetQueue().LockQueue() + break + } + } + // Creating channel + chNewMessage := make(chan interface{}) + s.MessageEventBus.Subscribe("file", chNewMessage) + replies.GetQueue().UnlockQueue() + defer s.MessageEventBus.UnSubscribe("file", chNewMessage) + + for { + select { + case <-timer.C: + s.Logger.Warn().Msg("No message received in Wait file (timeout).") + return nil + case <-chNewMessage: + //Checks message presence in two steps to avoid accessing fields of + s.Logger.Debug().Msg("New message received in Wait file.") + cell := replies.Get() + if cell == nil { + break + } + message := cell.GetMessage() + if message == nil { + break + } + fileMsg := message.(msg.FileMessage) + if args["fileName"] == VOID || fileMsg.FileName == args["fileName"] { + return &message + } + } + } +} diff --git a/handler_forward_ack.go b/handler_forward_ack.go new file mode 100644 index 0000000..86f307b --- /dev/null +++ b/handler_forward_ack.go @@ -0,0 +1,87 @@ +package shoset + +import ( + "errors" + "time" + + "github.com/ditrit/shoset/msg" + "github.com/rs/zerolog/log" +) + +// ForwardAcknownledgeHandler implements MessageHandlers interface. +type ForwardAcknownledgeHandler struct{} + +// Get returns the message for a given ShosetConn. +func (fah *ForwardAcknownledgeHandler) Get(c *ShosetConn) (msg.Message, error) { + var m msg.ForwardAck + err := c.GetReader().ReadMessage(&m) + return m, err +} + +// HandleDoubleWay handles message for a ShosetConn accordingly. +func (fah *ForwardAcknownledgeHandler) HandleDoubleWay(c *ShosetConn, message msg.Message) error { + m := message.(msg.ForwardAck) + if notInQueue := c.GetShoset().Queue["forwardAck"].Push(m, c.GetRemoteShosetType(), c.GetLocalAddress()); !notInQueue { + return errors.New("failed to handle forwardAck") + } + + c.GetShoset().MessageEventBus.Publish("forwardAck", true) // Notifies of the reception of a new message + + return nil +} + +// Send sends the message through the given Shoset network. +func (fah *ForwardAcknownledgeHandler) Send(s *Shoset, m msg.Message) { + // no-op + log.Warn().Msg("ForwardAcknownledgeHandler.Send not implemented") +} + +// Wait returns the message received for a given Shoset. +func (fah *ForwardAcknownledgeHandler) Wait(s *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { + timer := time.NewTimer(time.Duration(timeout) * time.Second) + + // Checks every message in the queue before waiting for a new message + //Checks message presence in two steps to avoid accessing attributs of + for { + cell := replies.Get() + if cell != nil { + message := cell.GetMessage() + if message != nil { + return &message + } + } else { + // Locking Queue to avoid missing a message while preparing the channel to receive events. + replies.GetQueue().LockQueue() + break + } + } + + // Creating channel + chNewMessage := make(chan interface{}) + s.MessageEventBus.Subscribe("forwardAck", chNewMessage) + replies.GetQueue().UnlockQueue() + defer s.MessageEventBus.UnSubscribe("forwardAck", chNewMessage) + + for { + select { + case <-timer.C: + s.Logger.Warn().Msg("No message received in Wait ForwardAck (timeout).") + return nil + case <-chNewMessage: + //Checks message presence in two steps to avoid accessing attributs of + cell := replies.Get() + if cell == nil { + break + } + message := cell.GetMessage() + if message == nil { + break + } + forwardAck := message.(msg.ForwardAck) + // Checks that it is a forwardAck for the right message. + if forwardAck.OGMessageUUID == args["UUID"] { + return &message + } + } + } +} diff --git a/handler_pki_event.go b/handler_pki_event.go new file mode 100644 index 0000000..f816ce2 --- /dev/null +++ b/handler_pki_event.go @@ -0,0 +1,232 @@ +package shoset + +import ( + "crypto/rsa" + "crypto/tls" + "errors" + "io/ioutil" + "path/filepath" + "time" + + "github.com/ditrit/shoset/msg" + "github.com/rs/zerolog/log" +) + +// PkiEventHandler implements MessageHandlers interface. +type PkiEventHandler struct{} + +// Get returns the message for a given ShosetConn. +func (peh *PkiEventHandler) Get(c *ShosetConn) (msg.Message, error) { + var evt msg.PkiEvent + err := c.GetReader().ReadMessage(&evt) + return evt, err +} + +// RunPkiRequest is the first step to get certified. +// Creates certificateRequest and send it as an event in the network to reach CA and get certified. +func (c *ShosetConn) RunPkiRequest(address string) error { + certificateRequest, hostPublicKey, hostPrivateKey, err := PrepareCertificate() + if err != nil { + c.Logger.Error().Msg("prepare certificate didn't work : " + err.Error()) + return errors.New("prepare certificate didn't work" + err.Error()) + } + path := filepath.Join(c.GetShoset().ConnsByLname.GetConfig().GetBaseDirectory(), c.GetShoset().ConnsByLname.GetConfig().GetFileName(), PATH_PRIVATE_KEY) + err = EncodeFile(hostPrivateKey, RSA_PRIVATE_KEY, path) + if err != nil { + c.Logger.Error().Msg(err.Error()) + return err + } + + pkiEvent := msg.NewPkiEventInit(TLS_SINGLE_WAY_PKI_EVT, address, c.GetShoset().GetLogicalName(), certificateRequest, hostPublicKey) + for { + pkiConn, err := tls.Dial(CONNECTION_TYPE, c.GetRemoteAddress(), c.GetShoset().tlsConfigSingleWay) + if err != nil { + time.Sleep(time.Millisecond * time.Duration(10)) + continue + } + c.UpdateConn(pkiConn) + + err = c.GetWriter().SendMessage(*pkiEvent) + if err != nil { + c.Logger.Error().Msg("couldn't send pkievt_TLSsingleWay: " + err.Error()) + } + + err = c.ReceiveMessage() + if err != nil { + continue + } + + c.Logger.Debug().Msg("RunPkiRequest: socket closed") + c.GetConn().Close() + return nil + } +} + +// HandleSingleWay handle receive message as a pki event. +// Then, based on the message's command, it handles message value adequately. +func (c *ShosetConn) HandleSingleWay(messageValue msg.Message) error { + evt := messageValue.(msg.PkiEvent) + switch { + case evt.GetCommand() == TLS_SINGLE_WAY_PKI_EVT: + err := c.HandlePkiRequest(messageValue) + if err != nil { + return err + } + case evt.GetCommand() == TLS_SINGLE_WAY_RETURN_PKI_EVT: + err := c.HandlePkiResponse(messageValue) + if err != nil { + return err + } + default: + c.Logger.Error().Msg("wrong cmd : " + evt.GetCommand()) + return errors.New("wrong cmd : " + evt.GetCommand()) + } + return nil +} + +// HandlePkiRequest handles a pki request. +// Depending if the Shoset is PKI or not, certify or send event back in network. +func (c *ShosetConn) HandlePkiRequest(messageValue msg.Message) error { + evt := messageValue.(msg.PkiEvent) + + if !c.GetShoset().GetIsPki() { + c.GetShoset().ConnsSingleConn.Store(evt.GetRequestAddress(), c) + handler := c.GetShoset().Handlers[TLS_SINGLE_WAY_PKI_EVT] + evt.SetCommand(TLS_DOUBLE_WAY_PKI_EVT) + handler.Send(c.GetShoset(), evt) + return nil + } + + certificateDirectory := filepath.Join(c.GetShoset().ConnsByLname.GetConfig().GetBaseDirectory(), c.GetShoset().ConnsByLname.GetConfig().GetFileName(), PATH_CA_CERT) + CAcertificate, err := ioutil.ReadFile(certificateDirectory) + if err != nil { + c.Logger.Error().Msg("couldn't get CAcertificate: " + err.Error()) + return err + } + + signedCertificate := c.GetShoset().SignCertificate(evt.GetCertificateRequest(), evt.GetHostPublicKey()) + if signedCertificate == nil { + c.Logger.Error().Msg("signCertificate didn't work") + return errors.New("signCertificate didn't work") + } + + var CAprivateKey *rsa.PrivateKey + if c.GetShoset().GetLogicalName() == evt.GetLogicalName() { // same logical name than CA, add CAprivateKey to the return event + CAprivateKey, err = GetPrivateKey(filepath.Join(c.GetShoset().ConnsByLname.GetConfig().GetBaseDirectory(), c.GetShoset().ConnsByLname.GetConfig().GetFileName(), PATH_CA_PRIVATE_KEY)) + if err != nil { + c.Logger.Error().Msg("couldn't get CAprivateKey : " + err.Error()) + return err + } + } + + pkiEventResponse := msg.NewPkiEventReturn(TLS_SINGLE_WAY_RETURN_PKI_EVT, evt.GetRequestAddress(), signedCertificate, CAcertificate, CAprivateKey) + err = c.GetWriter().SendMessage(*pkiEventResponse) + if err != nil { + c.Logger.Error().Msg("couldn't send singleConn returnpkievt: " + err.Error()) + return err + } + return nil +} + +// HandlePkiResponse encodes received signed certificate and sets up TLS Double Way for the Shoset. +func (c *ShosetConn) HandlePkiResponse(messageValue msg.Message) error { + evt := messageValue.(msg.PkiEvent) + err := EncodeFile(evt.GetSignedCert(), CERTIFICATE, filepath.Join(c.GetShoset().ConnsByLname.GetConfig().GetBaseDirectory(), c.GetShoset().ConnsByLname.GetConfig().GetFileName(), PATH_CERT)) + if err != nil { + c.Logger.Error().Msg(err.Error()) + return err + } + + CApath := filepath.Join(c.GetShoset().ConnsByLname.GetConfig().GetBaseDirectory(), c.GetShoset().ConnsByLname.GetConfig().GetFileName(), PATH_CA_CERT) + err = ioutil.WriteFile(CApath, evt.GetCAcert(), 0644) + if err != nil { + c.Logger.Error().Msg("couldn't write CAcertificate: " + err.Error()) + return err + } + + if evt.GetCAprivateKey() != nil { // same logical name than CA, so the Shoset becomes CA as well + path := filepath.Join(c.GetShoset().ConnsByLname.GetConfig().GetBaseDirectory(), c.GetShoset().ConnsByLname.GetConfig().GetFileName(), PATH_CA_PRIVATE_KEY) + err = EncodeFile(evt.GetCAprivateKey(), RSA_PRIVATE_KEY, path) + if err != nil { + c.Logger.Error().Msg(err.Error()) + return err + } + c.GetShoset().SetIsPki(true) + } + + err = c.GetShoset().SetUpDoubleWay() + c.GetShoset().SetIsValid(true) + if err != nil { + c.Logger.Error().Msg(err.Error()) + return err + } + return nil +} + +// HandleDoubleWay handles message for a ShosetConn accordingly. +func (peh *PkiEventHandler) HandleDoubleWay(c *ShosetConn, message msg.Message) error { + evt := message.(msg.PkiEvent) + + singleWayCertReqConn, _ := c.GetShoset().ConnsSingleConn.Load(evt.GetRequestAddress()) + switch { + case evt.GetCommand() == TLS_DOUBLE_WAY_PKI_EVT && c.GetShoset().GetIsPki(): + // this shoset is PKI and a TLSdoubleWay shoset sent a cert request. + // handle this cert request and return the signed cert. + if evt.GetCertificateRequest() == nil { + c.Logger.Error().Msg("empty cert req received") + return errors.New("empty cert req received") + } + + cfgDirectory := filepath.Join(c.GetShoset().ConnsByLname.GetConfig().GetBaseDirectory(), c.GetShoset().ConnsByLname.GetConfig().GetFileName(), PATH_CA_CERT) + CAcertificate, err := ioutil.ReadFile(cfgDirectory) + if err != nil { + return err + } + + signedCertificate := c.GetShoset().SignCertificate(evt.GetCertificateRequest(), evt.GetHostPublicKey()) + if signedCertificate == nil { + c.Logger.Error().Msg("CA failed to sign cert") + return errors.New("CA failed to sign cert") + } + + pkiEventResponse := msg.NewPkiEventReturn("return_pkievt_TLSdoubleWay", evt.GetRequestAddress(), signedCertificate, CAcertificate, nil) + pkiEventResponse.SetUUID(evt.GetUUID() + "*") // return event has the same uuid so that network isn't flooded with same events. + peh.Send(c.GetShoset(), *pkiEventResponse) + + case evt.GetCommand() == "return_pkievt_TLSdoubleWay" && singleWayCertReqConn != nil: + // this shoset received a signed cert from a TLSdoubleWay shoset destined to a TLSsingleWay shoset known by this one. + // send back the signed cert to the destined TLSsingleWay shoset. + evt.SetCommand(TLS_SINGLE_WAY_RETURN_PKI_EVT) + if err := singleWayCertReqConn.(*ShosetConn).GetWriter().SendMessage(evt); err != nil { + singleWayCertReqConn.(*ShosetConn).Logger.Warn().Msg("couldn't send returnpkievt : " + err.Error()) + return err + } + c.GetShoset().ConnsSingleConn.Delete(evt.GetRequestAddress()) + + default: + // this shoset is not PKI and a TLSdoubleWay shoset sent a cert request. + // sends back the cert request into the network until it reaches a PKI if it is not in it yet. + if notInQueue := c.GetShoset().Queue["pkievt_TLSdoubleWay"].Push(evt, c.GetRemoteShosetType(), c.GetLocalAddress()); notInQueue { + peh.Send(c.GetShoset(), evt) + } + } + return nil +} + +// Send sends the message through the given Shoset network. +func (peh *PkiEventHandler) Send(s *Shoset, evt msg.Message) { + s.ConnsByLname.Iterate( + func(lname string, ipAddress string, conn interface{}) { + if err := conn.(*ShosetConn).GetWriter().SendMessage(evt); err != nil { + conn.(*ShosetConn).Logger.Warn().Msg("couldn't send pkievt_TLSsingleWay : " + err.Error()) + } + }, + ) +} + +// Wait returns the message received for a given Shoset. +func (peh *PkiEventHandler) Wait(s *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { + // no-op + log.Warn().Msg("PkiEventHandler.Wait not implemented") + return nil +} diff --git a/handler_routing_event.go b/handler_routing_event.go new file mode 100644 index 0000000..3023f7d --- /dev/null +++ b/handler_routing_event.go @@ -0,0 +1,67 @@ +package shoset + +import ( + "github.com/ditrit/shoset/msg" + "github.com/rs/zerolog/log" +) + +// RoutingEventHandler implements MessageHandlers interface. +type RoutingEventHandler struct{} + +// Get returns the message for a given ShosetConn. +func (reh *RoutingEventHandler) Get(c *ShosetConn) (msg.Message, error) { + var routingEvt msg.RoutingEvent + err := c.GetReader().ReadMessage(&routingEvt) + return routingEvt, err +} + +// HandleDoubleWay handles message for a ShosetConn accordingly. +func (reh *RoutingEventHandler) HandleDoubleWay(c *ShosetConn, message msg.Message) error { + // Avoids using conns that are not yet stored and have missing informations (No remote Lname). + if !c.GetIsValid() { + return nil + } + + routingEvt := message.(msg.RoutingEvent) + + // Not saving Route to the same Lname + if c.GetLocalLogicalName() == routingEvt.GetOrigin() { + return nil + } + + // Tries to load the existing Route. + value, ok := c.GetShoset().RouteTable.Load(routingEvt.GetOrigin()) + + if !ok { + c.GetShoset().SaveRoute(c, &routingEvt) + return nil + } else if (value.(Route).GetUUID() != routingEvt.GetUUID()) && (routingEvt.GetRerouteTimestamp() > value.(Route).GetTimestamp()) { // if this route is shorter + c.GetShoset().SaveRoute(c, &routingEvt) + return nil + } else if routingEvt.GetNbSteps() < value.(Route).GetNbSteps() { // if the number of steps is less (accurate ? -> I don't think so because can be closer) + c.GetShoset().SaveRoute(c, &routingEvt) + return nil + } + return nil +} + +// Send sends the message through the given Shoset network. +func (reh *RoutingEventHandler) Send(s *Shoset, evt msg.Message) { + if s.GetIsValid() { + s.ConnsByLname.Iterate( + func(lname string, ipAddress string, conn interface{}) { + err := conn.(*ShosetConn).GetWriter().SendMessage(evt) + if err != nil { + s.Logger.Warn().Msg("couldn't send routingEvent : " + err.Error()) + } + }, + ) + } +} + +// Wait returns the message received for a given Shoset. +func (reh *RoutingEventHandler) Wait(s *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { + // no-op + log.Warn().Msg("RoutingEventHandler.Wait not implemented") + return nil +} diff --git a/handler_simple_message.go b/handler_simple_message.go new file mode 100644 index 0000000..4732b82 --- /dev/null +++ b/handler_simple_message.go @@ -0,0 +1,81 @@ +package shoset + +import ( + "errors" + "time" + + "github.com/ditrit/shoset/msg" +) + +// SimpleMessageHandler implements MessageHandlers interface. +type SimpleMessageHandler struct{} + +// Get returns the message for a given ShosetConn. +func (smh *SimpleMessageHandler) Get(c *ShosetConn) (msg.Message, error) { + var m msg.SimpleMessage + err := c.GetReader().ReadMessage(&m) + return m, err +} + +// HandleDoubleWay handles message for a ShosetConn accordingly. +func (smh *SimpleMessageHandler) HandleDoubleWay(c *ShosetConn, message msg.Message) error { + m := message.(msg.SimpleMessage) + + if notInQueue := c.GetShoset().Queue["simpleMessage"].Push(m, c.GetRemoteShosetType(), c.GetLocalAddress()); !notInQueue { + return errors.New("failed to handle simpleMessage") + } + + c.GetShoset().MessageEventBus.Publish("simpleMessage", true) // Notifies of the reception of a new message + + return nil +} + +// Send sends the message through the given Shoset network. +func (smh *SimpleMessageHandler) Send(s *Shoset, m msg.Message) { + s.forwardMessage(m) +} + +// Wait returns the message received for a given Shoset. +func (smh *SimpleMessageHandler) Wait(s *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message { + timer := time.NewTimer(time.Duration(timeout) * time.Second) + + // Checks every message in the queue before waiting for a new message + // Checks message presence in two steps to avoid accessing attributs of + for { + cell := replies.Get() + if cell != nil { + message := cell.GetMessage() + if message != nil { + return &message + } + } else { + // Locking Queue to avoid missing a message while preparing the channel to receive events. + replies.GetQueue().LockQueue() + break + } + } + // Creating channel + chNewMessage := make(chan interface{}) + s.MessageEventBus.Subscribe("simpleMessage", chNewMessage) + replies.GetQueue().UnlockQueue() + defer s.MessageEventBus.UnSubscribe("simpleMessage", chNewMessage) + + for { + select { + case <-timer.C: + s.Logger.Warn().Msg("No message received in Wait SimpleMessage (timeout).") + return nil + case <-chNewMessage: + //Checks message presence in two steps to avoid accessing fields of + cell := replies.Get() + if cell == nil { + break + } + message := cell.GetMessage() + if message == nil { + break + } + return &message + } + } +} diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..6ac7d7e --- /dev/null +++ b/logger.go @@ -0,0 +1,50 @@ +package shoset + +import ( + "fmt" + "os" + "strings" + "time" + + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +// SetLogLevel switches between log levels +func SetLogLevel(lv string) { + switch strings.ToLower(lv) { + default: + fallthrough + case INFO: + log.Logger = log.Level(zerolog.InfoLevel) + case TRACE: + log.Logger = log.Level(zerolog.TraceLevel) + case DEBUG: + log.Logger = log.Level(zerolog.DebugLevel) + case WARN: + log.Logger = log.Level(zerolog.WarnLevel) + case ERROR: + log.Logger = log.Level(zerolog.ErrorLevel) + } +} + +// LogWithCaller adds file and line number to log +func LogWithCaller() { + log.Logger = log.With().Caller().Logger() +} + +// InitPrettyLogger overrides log with prettier syntax +func InitPrettyLogger(colored bool) { + // define TimeFormat with predefined layout RFC3339Nano + output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339Nano, NoColor: !colored} + output.FormatLevel = func(i interface{}) string { + return strings.ToUpper(fmt.Sprintf("| %-6s|", i)) + } + log.Logger = log.Output(output) + LogWithCaller() +} + +// Log : default log +func Log(msg string) { + log.Info().Msg(msg) +} diff --git a/mapsafe.go b/mapsafe.go deleted file mode 100644 index 1e8fe25..0000000 --- a/mapsafe.go +++ /dev/null @@ -1,55 +0,0 @@ -package shoset - -import "sync" - -// MapSafe : simple key map safe for goroutines... -type MapSafe struct { - m map[string]interface{} - sync.Mutex -} - -// NewMapSafe : constructor -func NewMapSafe() *MapSafe { - m := new(MapSafe) - m.m = make(map[string]interface{}) - return m -} - -// Get : Get a value from a MapSafe -func (m *MapSafe) Get(key string) interface{} { - m.Lock() - defer m.Unlock() - return m.m[key] -} - -// Set : assign a value to a MapSafe -func (m *MapSafe) Set(key string, value interface{}) *MapSafe { - m.Lock() - m.m[key] = value - m.Unlock() - return m -} - -// Delete : delete a value in a MapSafe -func (m *MapSafe) Delete(key string) { - m.Lock() - _, ok := m.m[key] - if ok { - delete(m.m, key) - } - m.Unlock() -} - -// Iterate : iterate through MapSafe Values using a function -func (m *MapSafe) Iterate(iter func(string, interface{})) { - m.Lock() - for key, val := range m.m { - iter(key, val) - } - m.Unlock() -} - -// Len : return length of the map -func (m *MapSafe) Len() int { - return len(m.m) -} diff --git a/mapsafe_test.go b/mapsafe_test.go deleted file mode 100644 index 0ddf693..0000000 --- a/mapsafe_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package shoset_test - -import ( - "fmt" - "strconv" - "testing" - "time" - - "github.com/ditrit/shoset" -) - -// TestMapSafeCRUD : test MapSafe crud functions -func TestMapSafeCRUD(t *testing.T) { - m := shoset.NewMapSafe() - m.Set("a", 23).Set("b", 43).Set("c", 11) - fmt.Printf("Initial state : ") - m.Iterate( - func(key string, val interface{}) { - fmt.Printf(" - %s: %d\n", key, val.(int)) - }, - ) - m.Set("a", 454433) - fmt.Printf("After update : ") - m.Iterate( - func(key string, val interface{}) { - fmt.Printf(" - %s: %d\n", key, val.(int)) - }, - ) - m.Delete("b") - fmt.Printf("After delete : ") - m.Iterate( - func(key string, val interface{}) { - fmt.Printf(" - %s: %d\n", key, val.(int)) - }, - ) -} - -// TestFold : test MapSafe folding using closure -func TestFold(t *testing.T) { - m := shoset.NewMapSafe() - m.Set("a", 23).Set("b", 43).Set("c", 11) - str := "" - m.Iterate( - func(key string, val interface{}) { - str = fmt.Sprintf("%s, %d", str, val.(int)) - fmt.Printf(" - %s: %d\n", key, val.(int)) - }) - fmt.Printf("strValue : %s\n", str) -} - -// TestConcurrency -func TestConcurrency(t *testing.T) { - m := shoset.NewMapSafe() - for i := 0; i < 10; i++ { - m.Set(strconv.Itoa(i), i) - } - fmt.Printf("test Concurrency\n") - go m.Iterate( - func(key string, val interface{}) { - time.Sleep(time.Millisecond * time.Duration(10)) - fmt.Printf("%s, %d\n", key, val.(int)) - }) - time.Sleep(time.Millisecond * time.Duration(20)) - fmt.Printf("after Iterate\n") - m.Set("a", 101) - m.Set("b", 102) - fmt.Printf("after Set\n") - m.Iterate( - func(key string, val interface{}) { - fmt.Printf("%s, %d\n", key, val.(int)) - }) - -} diff --git a/mapsafebool.go b/mapsafebool.go deleted file mode 100644 index b1683f9..0000000 --- a/mapsafebool.go +++ /dev/null @@ -1,55 +0,0 @@ -package shoset - -import "sync" - -// MapSafeBool : simple key map safe for goroutines... -type MapSafeBool struct { - m map[string]bool - sync.Mutex -} - -// NewMapSafeBool : constructor -func NewMapSafeBool() *MapSafeBool { - m := new(MapSafeBool) - m.m = make(map[string]bool) - return m -} - -// Get : Get a value from a MapSafeBool -func (m *MapSafeBool) Get(key string) bool { - m.Lock() - defer m.Unlock() - return m.m[key] -} - -// Set : assign a value to a MapSafeBool -func (m *MapSafeBool) Set(key string, value bool) *MapSafeBool { - m.Lock() - m.m[key] = value - m.Unlock() - return m -} - -// Delete : delete a value in a MapSafeBool -func (m *MapSafeBool) Delete(key string) { - m.Lock() - _, ok := m.m[key] - if ok { - delete(m.m, key) - } - m.Unlock() -} - -// Iterate : iterate through MapSafeBool Values using a function -func (m *MapSafeBool) Iterate(iter func(string, bool)) { - m.Lock() - for key, val := range m.m { - iter(key, val) - } - m.Unlock() -} - -// Len : return length of the map -func (m *MapSafeBool) Len() int { - return len(m.m) -} diff --git a/mapsafeconn.go b/mapsafeconn.go deleted file mode 100644 index fc4fe1a..0000000 --- a/mapsafeconn.go +++ /dev/null @@ -1,102 +0,0 @@ -package shoset - -import ( - "sync" -) - -// MapSafeConn : simple key map safe for goroutines... -type MapSafeConn struct { - m map[string]*ShosetConn - sync.Mutex -} - -// NewMapSafeConn : constructor -func NewMapSafeConn() *MapSafeConn { - m := new(MapSafeConn) - m.m = make(map[string]*ShosetConn) - return m -} - -// Get : Get Map -func (m *MapSafeConn) GetM() map[string]*ShosetConn { - m.Lock() - defer m.Unlock() - return m.m -} - -// Get : Get a value from a MapSafeConn -func (m *MapSafeConn) GetByType(shosetType string) []*ShosetConn { - var result []*ShosetConn - m.Lock() - for _, val := range m.m { - if val.GetRemoteShosetType() == shosetType { - result = append(result, val) - } - } - m.Unlock() - return result -} - -// Get : Get a value from a MapSafeConn -func (m *MapSafeConn) Get(key string) *ShosetConn { - m.Lock() - defer m.Unlock() - return m.m[key] -} - -// Set : assign a value to a MapSafeConn -func (m *MapSafeConn) Set(key string, value *ShosetConn) *MapSafeConn { - m.Lock() - // fmt.Println("Address set") - m.m[key] = value - m.Unlock() - return m -} - -func (m *MapSafeConn) _keys(dir string) []string { // list of addresses - addresses := make([]string, m.Len()+1) - i := 0 - for key := range m.m { - if dir != "all" { - if m.m[key].GetDir() == dir { // on ne veut pas le in du join - addresses[i] = key - i++ - } - } else { - addresses[i] = key - i++ - } - } - return addresses[:i] -} - -func (m *MapSafeConn) Keys(dir string) []string { // list of addresses - m.Lock() - defer m.Unlock() - return m._keys(dir) - -} - -// Delete : delete a value in a MapSafeConn -func (m *MapSafeConn) Delete(key string) { - m.Lock() - _, ok := m.m[key] - if ok { - delete(m.m, key) - } - m.Unlock() -} - -// Iterate : iterate through MapSafeConn Values using a function -func (m *MapSafeConn) Iterate(iter func(string, *ShosetConn)) { - m.Lock() - for key, val := range m.m { - iter(key, val) - } - m.Unlock() -} - -// Len : return length of the map -func (m *MapSafeConn) Len() int { - return len(m.m) -} diff --git a/mapsafemapconn.go b/mapsafemapconn.go deleted file mode 100644 index 50b4cf6..0000000 --- a/mapsafemapconn.go +++ /dev/null @@ -1,176 +0,0 @@ -package shoset - -import ( - "fmt" - "os" - "sync" - - "github.com/spf13/viper" -) - -// MapSafeMapConn : simple key map safe for goroutines... -type MapSafeMapConn struct { - m map[string]*MapSafeConn - sync.Mutex - ConfigName string - viperConfig *viper.Viper -} - -// NewMapSafeMapConn : constructor -func NewMapSafeMapConn() *MapSafeMapConn { - m := new(MapSafeMapConn) - m.m = make(map[string]*MapSafeConn) - return m -} - -// Get : Get a value from a MapSafeMapConn -func (m *MapSafeMapConn) Get(key string) *MapSafeConn { - m.Lock() - defer m.Unlock() - return m.m[key] -} - -func (m *MapSafeMapConn) GetConfig() ([]string, []string) { - m.Lock() - defer m.Unlock() - return m._getConfig() -} - -func (m *MapSafeMapConn) _getConfig() ([]string, []string) { - return m.viperConfig.GetStringSlice("join"), m.viperConfig.GetStringSlice("link") -} - -// Set : assign a value to a MapSafeMapConn -func (m *MapSafeMapConn) Set(lname, key, protocolType, shosetType string, value *ShosetConn) *MapSafeMapConn { - m.Lock() - defer m.Unlock() - value.ch.LnamesByProtocol.Set(protocolType, lname) - value.ch.LnamesByType.Set(shosetType, lname) - - if lname != "" && key != "" { - if m.m[lname] == nil { - m.m[lname] = NewMapSafeConn() - } - m.m[lname].Set(key, value) - } - keys := m.m[lname].Keys("out") - if len(keys) != 0 { - m.updateFile(lname, protocolType, keys) - } - return m -} - -// Delete : delete a value in a MapSafeMapConn -func (m *MapSafeMapConn) Delete(lname, key string) { - m.Lock() - // var address string - _, ok := m.m[lname] - var lNamesByProtocol *MapSafeStrings - if ok { - shosetConn := m.m[lname].Get(key) - if shosetConn != nil { - // address = shosetConn.ch.GetBindAddress() - // fmt.Println(address, " enter delete") - lNamesByProtocol = shosetConn.ch.LnamesByProtocol - } - // if address == "127.0.0.1:8004" || address == "127.0.0.1:8002" { - // fmt.Println(address, " delete : ", key) - // } - m.m[lname].Delete(key) - } - - var protocolTypes []string - if lNamesByProtocol != nil { - lNamesByProtocol.Iterate( - func(protocol string, lNames map[string]bool) { - // if lname in lNames - if lNames[lname] && protocol == "bye" { - // if address == "127.0.0.1:8004" || address == "127.0.0.1:8002" { - // fmt.Println(address, " update file") - // } - remotesToJoin, remotesToLink := m._getConfig() - // fmt.Println(remotesToJoin, remotesToLink) - if contains(remotesToJoin, key) { - protocolTypes = append(protocolTypes, "join") - } - if contains(remotesToLink, key) { - protocolTypes = append(protocolTypes, "link") - } - - keys := m.m[lname].Keys("out") - for _, protocolType := range protocolTypes { - m.updateFile(lname, protocolType, keys) - } - - } - }, - ) - } - m.Unlock() -} - -func (m *MapSafeMapConn) updateFile(lname, protocolType string, keys []string) { - // if address == "127.0.0.1:8004" || address == "127.0.0.1:8002" { - // fmt.Println(address, "keys : ", keys) - // } - if m.ConfigName != "" { - m.viperConfig.Set(protocolType, keys) - dirname, err := os.UserHomeDir() - if err != nil { - fmt.Println(err) - } - m.viperConfig.WriteConfigAs(dirname + "/.shoset_config/" + m.ConfigName + ".yaml") - } -} - -func (m *MapSafeMapConn) SetConfigName(name string) { - if name != "" { - m.ConfigName = name - } -} - -// Iterate : iterate through MapSafeMapConn Values using a function -func (m *MapSafeMapConn) Iterate(lname string, iter func(string, *ShosetConn)) { - m.Lock() - mapConn := m.m[lname] - if mapConn != nil { - mapConn.Iterate(iter) - } - m.Unlock() -} - -func (m *MapSafeMapConn) IterateAll(iter func(string, *ShosetConn)) { - m.Lock() - for _, lname := range m._keys() { - mapConn := m.m[lname] - if mapConn != nil { - mapConn.Iterate(iter) - } - } - m.Unlock() -} - -// Len : return length of the map -func (m *MapSafeMapConn) Len() int { - return len(m.m) -} - -func (m *MapSafeMapConn) SetViper(viperConfig *viper.Viper) { - m.viperConfig = viperConfig -} - -func (m *MapSafeMapConn) _keys() []string { // list of logical names inside ConnsByName - lName := make([]string, m.Len()) - i := 0 - for key := range m.m { - lName[i] = key - i++ - } - return lName[:i] -} - -func (m *MapSafeMapConn) Keys() []string { // list of logical names inside ConnsByName - m.Lock() - defer m.Unlock() - return m._keys() -} diff --git a/mapsafestrings.go b/mapsafestrings.go deleted file mode 100644 index 4d317de..0000000 --- a/mapsafestrings.go +++ /dev/null @@ -1,89 +0,0 @@ -package shoset - -import ( - // "fmt" - // "fmt" - "sync" -) - -// MapSafeStrings : simple key map safe for goroutines... -type MapSafeStrings struct { - m map[string]map[string]bool - sync.Mutex -} - -// NewMapSafeStrings : constructor -func NewMapSafeStrings() *MapSafeStrings { - m := new(MapSafeStrings) - m.m = make(map[string]map[string]bool) - return m -} - -// func (m *MapSafeStrings) String() string { -// var descr string -// for key, lNames := range m.m { -// descr = descr + fmt.Sprintf("%s key : %s, lName : ", descr, key) -// for lName := range lNames { -// descr = fmt.Sprintf("%s %s", descr, lName) -// } -// descr = descr + "} \n\t\t\t" -// } -// return descr -// } - -// Get : Get a value from a MapSafeStrings -func (m *MapSafeStrings) Get(key string) map[string]bool { - m.Lock() - defer m.Unlock() - return m.m[key] -} - -func (m *MapSafeStrings) Set(key, value string) { - m.Lock() - defer m.Unlock() - if m.m[key] == nil { - m.m[key] = make(map[string]bool) - } - m.m[key][value] = true -} - -// Delete : delete a value in a MapSafeStrings -func (m *MapSafeStrings) Delete(key string) { - m.Lock() - _, ok := m.m[key] - if ok { - delete(m.m, key) - } - m.Unlock() -} - -// Iterate : iterate through MapSafeStrings Values using a function -func (m *MapSafeStrings) Iterate(iter func(string, map[string]bool)) { - m.Lock() - for key, val := range m.m { - iter(key, val) - } - m.Unlock() -} - -// Len : return length of the map -func (m *MapSafeStrings) Len() int { - return len(m.m) -} - -func (m *MapSafeStrings) Keys(key string) []string { - m.Lock() - defer m.Unlock() - return m._keys(key) -} - -func (m *MapSafeStrings) _keys(key string) []string { - lNamesByType := m.m[key] - lNames := make([]string, m.Len()) - i := 0 - for lName := range lNamesByType { - lNames[i] = lName - i++ - } - return lNames[:i] -} diff --git a/mapsyncmap.go b/mapsyncmap.go new file mode 100644 index 0000000..513566a --- /dev/null +++ b/mapsyncmap.go @@ -0,0 +1,156 @@ +package shoset + +import ( + "fmt" + "sync" + + "github.com/rs/zerolog/log" +) + +// MapSyncMap : safe for concurrent use by multiple goroutines without additional locking or coordination. +// Loads, stores, and deletes run in amortized constant time. +type MapSyncMap struct { + sync.Map // map[string]*sync.Map + cfg *Config // config file handler +} + +// GetConfig returns the config. +func (m *MapSyncMap) GetConfig() *Config { + return m.cfg +} + +// SetConfig sets the config with new one. +func (m *MapSyncMap) SetConfig(cfg *Config) { + m.cfg = cfg +} + +// updateFile updates config after being set +func (m *MapSyncMap) updateFile(protocol string, addresses []string) error { + m.cfg.AppendToKey(protocol, addresses) + + if err := m.cfg.WriteConfig(m.cfg.GetFileName()); err != nil { + log.Error().Msg("error writing config: " + err.Error()) + return err + } + return nil +} + +// StoreConfig sets the value for a key. +// It also updates the viper config file to keep new changes up to date. +// Overrides Store from sync.Map +func (m *MapSyncMap) StoreConfig(lName, address, protocol string, conn interface{}) error { + // Create a new if it doesn't exists + if syncMap, _ := m.Load(lName); syncMap == nil { + m.Store(lName, new(sync.Map)) + } + + syncMap, _ := m.Load(lName) + syncMap.(*sync.Map).Store(address, conn) + + // OUT is because we only handle the IPaddresses we had to protocol on at some point. + // They are the one we need to manually reconnect if a problem happens. + syncMap, _ = m.Load(lName) + addresses := Keys(syncMap.(*sync.Map), ALL) //OUT + log.Trace().Msg("Storing in config : " + protocol + " keys : " + fmt.Sprint(addresses)) + if len(addresses) != 0 { + err := m.updateFile(protocol, addresses) + return err + } + return nil +} + +// Iterate calls *MapSync.Range(iter) sequentially for each key and *MapSync present in the map. +func (m *MapSyncMap) Iterate(iter func(string, string, interface{})) { + if m != nil { + m.Range(func(key, syncMap interface{}) bool { + if syncMap != nil { + syncMap.(*sync.Map).Range(func(key2, val2 interface{}) bool { + iter(key.(string), key2.(string), val2) + return true + }) + } + return true + }) + } +} + +// Keys returns a []string corresponding to the keys from the map[string]*sync.Map object. +// sType set the specific keys depending on the desired shoset type. +func (m *MapSyncMap) Keys(sType string) []string { + var keys []string + if sType == ALL { + // all keys whatever the type from the ShosetConn + m.Range(func(key, _ interface{}) bool { + keys = append(keys, key.(string)) + return true + }) + } else { + // keys with specific ShosetConn type + m.Range(func(key, _ interface{}) bool { + if key == sType { + keys = append(keys, key.(string)) + } + return true + }) + } + return removeDuplicateStr(keys) +} + +// Apppend a value to a MapSyncMap : m[key1][key2]=value +func (m *MapSyncMap) AppendToKeys(key1 string, key2 string, value interface{}) { + if mapSync, ok := m.Load(key1); ok { + mapSync.(*sync.Map).Store(key2, value) + m.Store(key1, mapSync) + } else { + mapSync := new(sync.Map) + mapSync.Store(key2, value) + m.Store(key1, mapSync) + } +} + +func (m *MapSyncMap) String() string { + description := "" + if m != nil { + m.Range(func(key, syncMap interface{}) bool { + description += fmt.Sprintf("\tkey : %v", key) + if syncMap != nil { + syncMap.(*sync.Map).Range(func(key2, val2 interface{}) bool { + description += fmt.Sprintf("\n\t\t\tkey : %v val : %v", key2, val2) + return true + }) + } + description += "\n" + return true + }) + } + return description +} + +// Retrieve a value from a MapSyncMap : m[key1][key2]=value +func (m *MapSyncMap) LoadValueFromKeys(key1 string, key2 string) (interface{}, bool) { + if syncMap, ok := m.Load(key1); ok { + return syncMap.(*sync.Map).Load(key2) + } else { + return nil, false + } +} + +// Retrieve a value from a MapSyncMap : m[key1][key2]=value +func (m *MapSyncMap) DeleteValueFromKeys(key1 string, key2 string) { + if syncMap, ok := m.Load(key1); ok { + syncMap.(*sync.Map).Delete(key2) + + // Delete empty key1 + var i int + syncMap2, ok := m.Load(key1) + if ok { + syncMap2.(*sync.Map).Range(func(k, v interface{}) bool { + i++ + return i <= 0 // If i is no longer 0 there is no need to continue + }) + if i == 0 { + m.Delete(key1) + } + } + } +} diff --git a/mapsyncmap_test.go b/mapsyncmap_test.go new file mode 100644 index 0000000..fb84e79 --- /dev/null +++ b/mapsyncmap_test.go @@ -0,0 +1,38 @@ +package shoset + +import ( + "strconv" + "sync" + "testing" +) + +var shoset *Shoset = NewShoset("cl", "cl") + +func TestStoreConfig(t *testing.T) { + shoset.InitPKI("127.0.0.1:9001") + + direction := []string{IN, OUT} + + for i := 2; i < 6; i++ { + conn, err := NewShosetConn(shoset, "127.0.0.1:900"+strconv.Itoa(i), direction[i%len(direction)]) + if err != nil { + t.Errorf("StoreConfig didn't work, conn is nil") + } + err = shoset.ConnsByLname.StoreConfig("test", "127.0.0.1:900"+strconv.Itoa(i), "test_protocol", conn) + if err != nil { + t.Errorf("StoreConfig didn't work") + } + mapSync := new(sync.Map) + mapSync.Store("test", true) + shoset.LnamesByProtocol.Store("test_protocol", mapSync) + } +} + +func TestKeys(t *testing.T) { + TestStoreConfig(t) + + keys := shoset.ConnsByLname.Keys(ALL) + if keys[0] != "test" { + t.Errorf("Keys didn't work") + } +} diff --git a/msg/Iterator.go b/msg/Iterator.go index 255bcd8..416d2d4 100644 --- a/msg/Iterator.go +++ b/msg/Iterator.go @@ -16,6 +16,10 @@ type Iterator struct { func NewIterator(queue *Queue) *Iterator { i := new(Iterator) i.Init(queue) + + i.queue.m.Lock() // + defer i.queue.m.Unlock() /// + queue.iters[i] = true return i } @@ -75,3 +79,8 @@ func (i *Iterator) Get() *Cell { func (i *Iterator) PrintQueue() { i.queue.Print() } + +// PrintQueue : print la queue +func (i *Iterator) GetQueue() *Queue { + return i.queue +} diff --git a/msg/command.go b/msg/command.go index 013b128..2dcf57d 100644 --- a/msg/command.go +++ b/msg/command.go @@ -4,25 +4,25 @@ package msg type Command struct { MessageBase Target string - Command string + Command string // type of command Context map[string]interface{} } // NewCommand : Command constructor // todo : passer une map pour gerer les valeurs optionnelles ? func NewCommand(target string, command string, payload string) *Command { - c := new(Command) + c := &Command{ + MessageBase: MessageBase{Payload: payload}, + Target: target, + Context: make(map[string]interface{}), + Command: command, + } c.InitMessageBase() - - c.Target = target - c.Context = make(map[string]interface{}) - c.Command = command - c.Payload = payload return c } -// GetMsgType accessor -func (c Command) GetMsgType() string { return "cmd" } +// GetMessageType accessor +func (c Command) GetMessageType() string { return "cmd" } // GetTarget : func (c Command) GetTarget() string { return c.Target } diff --git a/msg/config.go b/msg/config.go index 383d9a0..121a4ce 100644 --- a/msg/config.go +++ b/msg/config.go @@ -5,7 +5,7 @@ package msg type Config struct { MessageBase Target string - Command string + Command string // type of config Context map[string]interface{} } @@ -22,8 +22,8 @@ func NewConfig(target string, command string, payload string) *Config { return c } -// GetMsgType accessor -func (c Config) GetMsgType() string { return "config" } +// GetMessageType accessor +func (c Config) GetMessageType() string { return "config" } // GetTarget : func (c Config) GetTarget() string { return c.Target } diff --git a/msg/configProtocol.go b/msg/configProtocol.go index 149ba93..c1dd266 100644 --- a/msg/configProtocol.go +++ b/msg/configProtocol.go @@ -1,29 +1,30 @@ package msg -// ConfigProtocol : Gandalf Socket config +// ConfigProtocol : config handler for each protocol of Shoset. type ConfigProtocol struct { MessageBase - CommandName string - LogicalName string - ShosetType string - Address string - MyBrothers []string - YourBrothers []string + CommandName string // type of config + LogicalName string // logical name of the Shoset + ShosetType string // type of the Shoset + BindAddress string // bindAddress of the Shoset + MyBrothers []string // list of ipAddresses of same Shoset logical name + YourBrothers []string // list of ipAddresses of other Shoset logical name } -// for link and join -func NewCfg(address, lName, shosetType, commandName string) *ConfigProtocol { +// NewConfigProtocol is a simple config for each protocol. +func NewConfigProtocol(address, lName, shosetType, commandName string) *ConfigProtocol { c := new(ConfigProtocol) c.InitMessageBase() c.CommandName = commandName - c.Address = address + c.BindAddress = address c.LogicalName = lName c.ShosetType = shosetType return c } -// for link -func NewCfgBrothers(myBrothers, yourBrothers []string, lName, commandName, shosetType string) *ConfigProtocol { +// NewConfigBrothersProtocol is dedicated to Link protocol.* +// +func NewConfigBrothersProtocol(myBrothers, yourBrothers []string, commandName, lName, shosetType string) *ConfigProtocol { c := new(ConfigProtocol) c.InitMessageBase() c.CommandName = commandName @@ -34,14 +35,12 @@ func NewCfgBrothers(myBrothers, yourBrothers []string, lName, commandName, shose return c } -// GetMsgType accessor -func (c ConfigProtocol) GetMsgType() string { +// GetMessageType returns MessageType from ConfigProtocol. +func (c ConfigProtocol) GetMessageType() string { switch c.GetCommandName() { case "join": return "cfgjoin" - case "aknowledge_join": - return "cfgjoin" - case "unaknowledge_join": + case "acknowledge_join": return "cfgjoin" case "member": return "cfgjoin" @@ -49,6 +48,8 @@ func (c ConfigProtocol) GetMsgType() string { return "cfglink" case "brothers": return "cfglink" + case "acknowledge_link": + return "cfglink" case "bye": return "cfgbye" case "delete": @@ -57,20 +58,20 @@ func (c ConfigProtocol) GetMsgType() string { return "Wrong input protocolType" } -// GetLogicalName : +// GetLogicalName returns LogicalName from ConfigProtocol. func (c ConfigProtocol) GetLogicalName() string { return c.LogicalName } -// GetShosetType : +// GetShosetType returns ShosetType from ConfigProtocol. func (c ConfigProtocol) GetShosetType() string { return c.ShosetType } -// GetAddress : -func (c ConfigProtocol) GetAddress() string { return c.Address } +// GetAddress returns BindAddress from ConfigProtocol. +func (c ConfigProtocol) GetAddress() string { return c.BindAddress } -// GetCommandName : +// GetCommandName returns CommandName from ConfigProtocol. func (c ConfigProtocol) GetCommandName() string { return c.CommandName } -// GetConns : +// GetMyBrothers returns MyBrothers from ConfigProtocol. func (c ConfigProtocol) GetMyBrothers() []string { return c.MyBrothers } -// GetBros : +// GetYourBrothers returns YourBrothers from ConfigProtocol. func (c ConfigProtocol) GetYourBrothers() []string { return c.YourBrothers } diff --git a/msg/event.go b/msg/event.go index 6f1f1ef..4b932fa 100644 --- a/msg/event.go +++ b/msg/event.go @@ -33,8 +33,8 @@ func NewEventClassic(topic, event, payload string) *Event { return NewEvent(tab) } -// GetMsgType accessor -func (e Event) GetMsgType() string { return "evt" } +// GetMessageType accessor +func (e Event) GetMessageType() string { return "evt" } // GetTopic : func (e Event) GetTopic() string { return e.Topic } diff --git a/msg/fileTransfer.go b/msg/fileTransfer.go new file mode 100644 index 0000000..82fc339 --- /dev/null +++ b/msg/fileTransfer.go @@ -0,0 +1,59 @@ +package msg + +type FileStateMessage struct { + UUID string + Name string + Hash string + Version int + Path string + LastOperation OperationMessage +} + +type OperationMessage struct { + Name string + File string + NewFile string + Version int + Hash string +} + +type FileMessage struct { + MessageBase + MessageName string + + // file description + FileName string + FileHash string + FileUUID string + + // for file info message + FileSize int64 + FileVersion int + FilePath string + FileOperation OperationMessage + FileHashMap map[int]string + + // for library message + Library []FileStateMessage + + // for chunk message + Begin int64 + Length int + ChunkData []byte + + // for have message + PieceNumber int + + // for bitfield message + PieceSize int + Bitfield []bool + + // to have information about the state of a connection + Rate int + MissingLength int64 + + // for externalCommands + AnswerLocked bool +} + +func (fileMessage FileMessage) GetMessageType() string { return "file" } diff --git a/msg/forwardAck.go b/msg/forwardAck.go new file mode 100644 index 0000000..4752dee --- /dev/null +++ b/msg/forwardAck.go @@ -0,0 +1,26 @@ +package msg + +// ForwardAck : acknowledges that the next shoset in the route to the destination received the message. +type ForwardAck struct { + MessageBase + OGMessageUUID string // UUID of acknowledged message + OGMessageTimeStamp int64 // TimeStamp of acknowledged message +} + +// NewSimpleMessage : ForwardAck constructor +func NewForwardAck(uuid string, timeStamp int64) *ForwardAck { + var c ForwardAck + c.InitMessageBase() + c.OGMessageUUID = uuid + c.OGMessageTimeStamp = timeStamp + return &c +} + +// GetMessageType accessor +func (c ForwardAck) GetMessageType() string { return "forwardAck" } + +// GetOGMessageUUID accessor +func (c ForwardAck) GetOGMessageUUID() string { return c.OGMessageUUID } + +// GetOGMessageTimeStamp accessor +func (c ForwardAck) GetOGMessageTimeStamp() int64 { return c.OGMessageTimeStamp } diff --git a/msg/message.go b/msg/message.go index ead8bdb..fe31165 100644 --- a/msg/message.go +++ b/msg/message.go @@ -8,7 +8,7 @@ import ( // Message interface type Message interface { - GetMsgType() string + GetMessageType() string GetUUID() string GetTenant() string GetToken() string @@ -17,66 +17,62 @@ type Message interface { GetPayload() string GetMajor() int8 GetMinor() int8 + + GetDestinationLname() string } // MessageBase base struct for messages type MessageBase struct { - UUID string - Tenant string - Token string - Timeout int64 - Timestamp int64 - Payload string - Next string - Major int8 - Minor int8 + UUID string // automatically generated id for a single message + Tenant string // tenant system, filter only messages for a specific tenant + Token string // ! refactor this attribute ! + Timeout int64 // time set to keep message in memory + Timestamp int64 // time when message is created (to the millesecond) + Payload string // handle args info + Next string // ! refactor this attribute ! + Major int8 // ?? + Minor int8 // ?? + DestinationLname string // LogicalName the message is destined to (For forwarding) } // InitMessageBase constructor func (m *MessageBase) InitMessageBase() { m.UUID = uuid.New() - m.Timestamp = time.Now().Unix() - m.Timeout = 1000 + m.Timestamp = time.Now().UnixMilli() + m.Timeout = 10 m.Major = 1 m.Minor = 0 } -// GetUUID accessor -func (m MessageBase) GetUUID() string { - return m.UUID -} +// GetUUID returns UUID from MessageBase. +func (m MessageBase) GetUUID() string { return m.UUID } -// GetTenant accessor -func (m MessageBase) GetTenant() string { - return m.Tenant -} +// SetUUID sets UUID for MessageBase. +func (m *MessageBase) SetUUID(newUUID string) { m.UUID = newUUID } -// GetToken accessor -func (m MessageBase) GetToken() string { - return m.Token -} +// GetTenant returns Tenant from MessageBase. +func (m MessageBase) GetTenant() string { return m.Tenant } -// GetTimestamp accessor -func (m MessageBase) GetTimestamp() int64 { - return m.Timestamp -} +// GetToken returns Token from MessageBase. +func (m MessageBase) GetToken() string { return m.Token } -// GetTimeout accessor -func (m MessageBase) GetTimeout() int64 { - return m.Timeout -} +// GetTimestamp returns Timestamp from MessageBase. +func (m MessageBase) GetTimestamp() int64 { return m.Timestamp } -// GetPayload accessor -func (m MessageBase) GetPayload() string { - return m.Payload -} +// GetTimeout returns Timeout from MessageBase. +func (m MessageBase) GetTimeout() int64 { return m.Timeout } -// GetMajor accessor -func (m MessageBase) GetMajor() int8 { - return m.Major -} +// GetPayload returns Payload from MessageBase. +func (m MessageBase) GetPayload() string { return m.Payload } -// GetMinor accessor -func (m MessageBase) GetMinor() int8 { - return m.Minor -} +// GetMajor returns Major from MessageBase. +func (m MessageBase) GetMajor() int8 { return m.Major } + +// GetMinor returns Minor from MessageBase. +func (m MessageBase) GetMinor() int8 { return m.Minor } + +// GetDestinationLname returns DestinationLname from MessageBase. +func (m MessageBase) GetDestinationLname() string { return m.DestinationLname } + +// Set DestinationLname sets DestinationLname from MessageBase. +func (m *MessageBase) SetDestinationLname(s string) { m.DestinationLname = s } diff --git a/msg/pkiEvent.go b/msg/pkiEvent.go new file mode 100644 index 0000000..61df31c --- /dev/null +++ b/msg/pkiEvent.go @@ -0,0 +1,93 @@ +package msg + +import ( + "crypto/rsa" + "crypto/x509" +) + +// PkiEvent : Event dedicated to PKI initialization. +type PkiEvent struct { + MessageBase + RequestAddress string // requesting address for certification + Command string // type of event + Secret string // secret shown to CA to authenticate + LogicalName string // logical name of the Shoset + CertificateRequest *x509.Certificate // certificate to be signed by the CA + SignedCertificate []byte // certificate signed by the CA + HostPublicKey *rsa.PublicKey // public key of the requesting Shoset + CAprivateKey *rsa.PrivateKey // private key of the responding CA + CAcertificate []byte // certificate of the CA +} + +// NewPkiEventInit creates a new init PkiEvent object. +// Initializes each fields and message base. +func NewPkiEventInit(command, requestAddress, logicalName string, certificateRequest *x509.Certificate, hostPublicKey *rsa.PublicKey) *PkiEvent { + e := new(PkiEvent) + e.InitMessageBase() + + e.RequestAddress = requestAddress + e.Command = command + e.CertificateRequest = certificateRequest + e.HostPublicKey = hostPublicKey + e.LogicalName = logicalName + return e +} + +// NewPkiEventReturn creates a new return PkiEvent object. +// Initializes each fields and message base. +func NewPkiEventReturn(command, requestAddress string, signedCertificate, CAcertificate []byte, caPrivateKey *rsa.PrivateKey) *PkiEvent { + e := new(PkiEvent) + e.InitMessageBase() + + e.Command = command + e.RequestAddress = requestAddress + e.SignedCertificate = signedCertificate + e.CAcertificate = CAcertificate + e.CAprivateKey = caPrivateKey + return e +} + +// GetMessageType returns MessageType from PkiEvent. +func (e PkiEvent) GetMessageType() string { + switch e.GetCommand() { + case "pkievt_TLSsingleWay": + return "pkievt_TLSsingleWay" + case "return_pkievt_TLSsingleWay": + return "pkievt_TLSsingleWay" + case "pkievt_TLSdoubleWay": + return "pkievt_TLSdoubleWay" + case "return_pkievt_TLSdoubleWay": + return "pkievt_TLSdoubleWay" + } + return "Wrong input protocolType" +} + +// GetSecret returns Secret from PkiEvent. +func (e PkiEvent) GetSecret() string { return e.Secret } + +// GetCommand returns Command from PkiEvent. +func (e PkiEvent) GetCommand() string { return e.Command } + +// SetCommand sets Command for PkiEvent. +func (e *PkiEvent) SetCommand(command string) { e.Command = command } + +// GetRequestAddress returns RequestAddress from PkiEvent. +func (e PkiEvent) GetRequestAddress() string { return e.RequestAddress } + +// GetLogicalName returns LogicalName from PkiEvent. +func (e PkiEvent) GetLogicalName() string { return e.LogicalName } + +// GetCertRequest returns CertificateRequest from PkiEvent. +func (e PkiEvent) GetCertificateRequest() *x509.Certificate { return e.CertificateRequest } + +// GetSignedCert returns SignedCertificate from PkiEvent. +func (e PkiEvent) GetSignedCert() []byte { return e.SignedCertificate } + +// GetHostPublicKey returns HostPublicKey from PkiEvent. +func (e PkiEvent) GetHostPublicKey() *rsa.PublicKey { return e.HostPublicKey } + +// GetCAprivateKey returns CAprivateKey from PkiEvent. +func (e PkiEvent) GetCAprivateKey() *rsa.PrivateKey { return e.CAprivateKey } + +// GetCAcert returns CAcertificate from PkiEvent. +func (e PkiEvent) GetCAcert() []byte { return e.CAcertificate } diff --git a/msg/queue.go b/msg/queue.go index 54a699d..60400dd 100644 --- a/msg/queue.go +++ b/msg/queue.go @@ -57,13 +57,9 @@ func (q *Queue) GetByReferencesUUID(uuid string) *Event { // Push : insert a new value in the queue except if the UUID is already present and remove after timeout expiration func (q *Queue) Push(m Message, RemoteShosetType, RemoteAddress string) bool { - fmt.Printf("Push a message!\n") - // Let's first initialize the Cell with all our data var c Cell c.key = m.GetUUID() - fmt.Println("key") - fmt.Println(c.key) c.timeout = m.GetTimeout() c.RemoteShosetType = RemoteShosetType c.RemoteAddress = RemoteAddress @@ -150,6 +146,10 @@ func (q *Queue) remove(key string) { // IsEmpty : the event queue is empty func (q *Queue) IsEmpty() bool { + + q.m.Lock() + defer q.m.Unlock() + return q.qlist.Len() == 0 } @@ -163,3 +163,11 @@ func (q *Queue) Print() { } fmt.Printf("nb cell : %d\n", q.qlist.Len()) } + +func (q *Queue) LockQueue() { + q.m.Lock() +} + +func (q *Queue) UnlockQueue() { + q.m.Unlock() +} diff --git a/msg/reader.go b/msg/reader.go index 4ded706..9e45f35 100644 --- a/msg/reader.go +++ b/msg/reader.go @@ -3,39 +3,41 @@ package msg import ( "bufio" "encoding/gob" - "fmt" "io" "sync" + + "github.com/rs/zerolog/log" ) -// Reader : simple bufio.Reader safe for goroutines... +// Reader : simple bufio.Reader safe for goroutines. type Reader struct { - b *bufio.Reader - m sync.Mutex + b *bufio.Reader // implements buffering for an io.Reader object + dec *gob.Decoder // manages the receipt of type and data information read from the remote side of a connection. It is safe for concurrent use by multiple goroutines + m sync.Mutex // mutex for goroutines synchronization } -// NewReader : constructor -func NewReader(rd io.Reader) *Reader { - s := new(Reader) - s.b = bufio.NewReader(rd) - return s +// UpdateReader updates reader object with new connection information. +func (r *Reader) UpdateReader(rd io.Reader) { + r.m.Lock() + defer r.m.Unlock() + r.b = bufio.NewReader(rd) + r.dec = gob.NewDecoder(r.b) } -// ReadString : safe version for goroutines +// ReadString reads until the first message in the input in a safe way for goroutines, returning a string containing the data. func (r *Reader) ReadString() (string, error) { r.m.Lock() defer r.m.Unlock() return r.b.ReadString('\n') } -// ReadMessage : decode a message in a safe way for goroutines +// ReadMessage decodes a message in a safe way for goroutines. func (r *Reader) ReadMessage(data interface{}) error { r.m.Lock() defer r.m.Unlock() - enc := gob.NewDecoder(r.b) - err := enc.Decode(data) + err := r.dec.Decode(data) if err != nil { - fmt.Printf("error in ReadMessage : %s\n", err) + log.Error().Msg("error in ReadMessage : " + err.Error()) } return err } diff --git a/msg/routingEvent.go b/msg/routingEvent.go new file mode 100644 index 0000000..a0d398a --- /dev/null +++ b/msg/routingEvent.go @@ -0,0 +1,48 @@ +package msg + +import "time" + +// RoutingEvent : to broadcast routes between logical names in the network +type RoutingEvent struct { + MessageBase + Origin string + NbSteps int + RerouteTimestamp int64 +} + +// NewRoutingEvent : RoutingEvent constructor +func NewRoutingEvent(origin string, GenerateTimestamp bool, RerouteTimestamp int64, uuid string) *RoutingEvent { + r := new(RoutingEvent) + r.InitMessageBase() + + r.Origin = origin + r.NbSteps = 1 + if uuid != "" { + r.SetUUID(uuid) + } + + if GenerateTimestamp { + r.SetRerouteTimestamp(time.Now().UnixMilli()) + } else { + r.SetRerouteTimestamp(RerouteTimestamp) + } + return r +} + +// GetMsgType accessor +func (r RoutingEvent) GetMessageType() string { return "routingEvent" } + +// GetOrigin accessor +func (r RoutingEvent) GetOrigin() string { return r.Origin } + +// GetNbSteps accessor +func (r RoutingEvent) GetNbSteps() int { return r.NbSteps } + +// SetNbSteps accessor +func (r *RoutingEvent) SetNbSteps(i int) { r.NbSteps = i } + +// GetRerouteTimestamp accessor +func (r RoutingEvent) GetRerouteTimestamp() int64 { return r.RerouteTimestamp } + +// SetRerouteTimestamp accessor +func (r *RoutingEvent) SetRerouteTimestamp(i int64) { r.RerouteTimestamp = i } diff --git a/msg/simpleMessage.go b/msg/simpleMessage.go new file mode 100644 index 0000000..90dbd8b --- /dev/null +++ b/msg/simpleMessage.go @@ -0,0 +1,19 @@ +package msg + +// SimpleMessage : MassageBase with forwardable features (handler) +type SimpleMessage struct { + MessageBase +} + +// NewSimpleMessage : SimpleMessage constructor +func NewSimpleMessage(target string, payload string) *SimpleMessage { + var c SimpleMessage + c.InitMessageBase() + + c.SetDestinationLname(target) + c.Payload=payload + return &c +} + +// GetMessageType accessor +func (c SimpleMessage) GetMessageType() string { return "simpleMessage" } diff --git a/msg/writer.go b/msg/writer.go index f794e29..fdfd003 100644 --- a/msg/writer.go +++ b/msg/writer.go @@ -4,57 +4,82 @@ import ( "bufio" "encoding/gob" "errors" - "fmt" "io" "sync" + "syscall" ) // Writer : simple bufio.Writer safe for goroutines... type Writer struct { - b *bufio.Writer - m sync.Mutex + b *bufio.Writer + enc *gob.Encoder + m sync.Mutex } -// NewWriter : constructor -func NewWriter(wd io.Writer) *Writer { - s := new(Writer) - s.b = bufio.NewWriter(wd) - return s +// UpdateWriter updates writer object with new connection information. +func (w *Writer) UpdateWriter(wd io.Writer) { + w.m.Lock() + defer w.m.Unlock() + w.b = bufio.NewWriter(wd) + w.enc = gob.NewEncoder(w.b) } -// WriteString : safe version for goroutines -func (r *Writer) WriteString(data string) (int, error) { - if r.b != nil { - r.m.Lock() - defer r.m.Unlock() - return r.b.WriteString(data + "\n") +// Flush writes any buffered data to the underlying io.Writer in a safe version for goroutines. +func (w *Writer) Flush() error { + if w.b != nil { + return w.b.Flush() } - return 0, errors.New("Writer not ready") + return errors.New("Writer not initialized") } -// Flush : safe version for goroutines -func (r *Writer) Flush() error { - if r.b != nil { - r.m.Lock() - defer r.m.Unlock() - return r.b.Flush() +// SendMessage send a message through a connection. +// Writes message type first. +// Then writes message. +func (w *Writer) SendMessage(msg Message) error { + w.m.Lock() + defer w.m.Unlock() + + _, err := w.WriteString(msg.GetMessageType()) + if err != nil { + if errors.Is(err, syscall.EPIPE) { + // https://gosamples.dev/broken-pipe/ + return nil + } else if errors.Is(err, syscall.ECONNRESET) { + // https://gosamples.dev/connection-reset-by-peer/ + return nil + } + return err } - return errors.New("Writer not initialized") -} -// WriteMessage : encode a message in a safe way for goroutines -func (r *Writer) WriteMessage(data interface{}) error { - if r.b != nil { - r.m.Lock() - defer r.m.Unlock() - enc := gob.NewEncoder(r.b) - err := enc.Encode(data) - r.b.Flush() - if err != nil { - fmt.Println(data) - fmt.Printf("error in Writing Message : %s\n", err) + err = w.WriteMessage(msg) + if err != nil { + if errors.Is(err, syscall.EPIPE) { + return nil + } else if errors.Is(err, syscall.ECONNRESET) { + return nil } return err } - return errors.New("Writer not initialized") + return nil +} + +// WriteString writes a string in a safe version for goroutines. +func (w *Writer) WriteString(data string) (int, error) { + if w.b != nil { + return w.b.WriteString(data + "\n") + } + return 0, errors.New("Writer not ready") +} + +// WriteMessage encodes a message in a safe way for goroutines. +func (w *Writer) WriteMessage(data interface{}) error { + if w.b == nil { + return errors.New("Writer not initialized") + } + + err := w.enc.Encode(data) + if err != nil { + return err + } + return w.Flush() } diff --git a/pki.go b/pki.go new file mode 100644 index 0000000..e0180e8 --- /dev/null +++ b/pki.go @@ -0,0 +1,278 @@ +package shoset + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "errors" + "io/ioutil" + "math/big" + "net" + "os" + "path/filepath" + "strings" + "time" + + "github.com/google/uuid" +) + +// InitPKI inits the concerned Shoset to get the role PKI which is basically admin of network. +// First inits CA role and then inits Shoset itself. +// Overload of shoset.Bind() function +func (s *Shoset) InitPKI(bindAddress string) { + ipAddress, err := GetIP(bindAddress) + if err != nil { + s.Logger.Error().Msg("wrong IP format : " + err.Error()) + return + } + formattedIpAddress := strings.Replace(ipAddress, ":", "_", -1) + formattedIpAddress = strings.Replace(formattedIpAddress, ".", "-", -1) // formats for filesystem to 127-0-0-1_8001 instead of 127.0.0.1:8001 + s.ConnsByLname.GetConfig().SetFileName(formattedIpAddress) + + if !s.IsCertified(filepath.Join(s.ConnsByLname.GetConfig().baseDirectory, formattedIpAddress)) { + s.initCA(formattedIpAddress) + s.initShoset(bindAddress) + } else { + err = s.SetUpDoubleWay() + if err != nil { + s.Logger.Error().Msg(err.Error()) + return + } + } + + if s.GetBindAddress() == VOID { + err := s.Bind(bindAddress) + if err != nil { + s.Logger.Error().Msg("couldn't set bindAddress : " + err.Error()) + return + } + } +} + +// initCA inits CA role for the concerned Shoset, who then become admin of network. +func (s *Shoset) initCA(formattedIpAddress string) { + s.SetIsPki(true) + + cfgDirectory, err := s.ConnsByLname.GetConfig().InitFolders(formattedIpAddress) + if err != nil { + s.Logger.Error().Msg("couldn't init folders : " + err.Error()) + return + } + fileName := s.ConnsByLname.GetConfig().GetFileName() + CApath := filepath.Join(cfgDirectory, fileName, PATH_CA_CERT) + s.Logger.Debug().Msg("CApath : " + CApath) + CAcertificate := &x509.Certificate{ + SerialNumber: big.NewInt(1653), + Subject: pkix.Name{ + Organization: []string{"Ditrit"}, + Country: []string{"33"}, + Province: []string{"France"}, + Locality: []string{"Paris"}, + StreetAddress: []string{"19 Rue Bergère"}, + PostalCode: []string{"75009"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + IsCA: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } + + CAprivateKey, _ := rsa.GenerateKey(rand.Reader, 2048) + err = EncodeFile(CAprivateKey, RSA_PRIVATE_KEY, filepath.Join(cfgDirectory, fileName, PATH_CA_PRIVATE_KEY)) + if err != nil { + s.Logger.Error().Msg(err.Error()) + return + } + + CApublicKey := &CAprivateKey.PublicKey + + signedCAcert, err := x509.CreateCertificate(rand.Reader, CAcertificate, CAcertificate, CApublicKey, CAprivateKey) + if err != nil { + s.Logger.Error().Msg("couldn't create CA : " + err.Error()) + return + } + + err = EncodeFile(signedCAcert, CERTIFICATE, CApath) + if err != nil { + s.Logger.Error().Msg(err.Error()) + return + } +} + +// initShoset inits certs for the Shoset with the previous created CA. +func (s *Shoset) initShoset(bindAddress string) { + fileName := s.ConnsByLname.GetConfig().GetFileName() + cfgDirectory := s.ConnsByLname.GetConfig().GetBaseDirectory() + + certificateRequest, hostPublicKey, hostPrivateKey, err := PrepareCertificate() + if err != nil { + s.Logger.Error().Msg("prepare certificate didn't work") + return + } + err = EncodeFile(hostPrivateKey, RSA_PRIVATE_KEY, filepath.Join(s.ConnsByLname.GetConfig().GetBaseDirectory(), s.ConnsByLname.GetConfig().GetFileName(), PATH_PRIVATE_KEY)) + if err != nil { + s.Logger.Error().Msg(err.Error()) + return + } + + signedHostCert := s.SignCertificate(certificateRequest, hostPublicKey) + if signedHostCert == nil { + s.Logger.Error().Msg("sign cert didn't work") + return + } + err = EncodeFile(signedHostCert, CERTIFICATE, filepath.Join(cfgDirectory, fileName, PATH_CERT)) + if err != nil { + s.Logger.Error().Msg(err.Error()) + return + } + + err = s.SetUpDoubleWay() + if err != nil { + s.Logger.Error().Msg(err.Error()) + return + } +} + +// GenerateSecret generates a secret based on a login and a password that the admin of the system will have created. +// Must be PKI to generate a secret. +func (s *Shoset) GenerateSecret(login, password string) string { + if s.GetIsPki() { + // use login and password. + return uuid.New().String() + } + return VOID +} + +// PrepareCertificate prepares certificates for a Shoset by returning certificateRequest, hostPublicKey and hostPrivateKey. +// To get more info about the generated cert, use : openssl x509 -in ./cert.crt -text -noout +func PrepareCertificate() (*x509.Certificate, *rsa.PublicKey, *rsa.PrivateKey, error) { + certificateRequest := &x509.Certificate{ + SerialNumber: big.NewInt(1658), + Subject: pkix.Name{ + Organization: []string{"Ditrit"}, + Country: []string{"33"}, + Province: []string{"France"}, + Locality: []string{"Paris"}, + StreetAddress: []string{"19 Rue Bergère"}, + PostalCode: []string{"75009"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + SubjectKeyId: []byte{1, 2, 3, 4, 6}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature, + } + + interfaceAddresses, err := net.InterfaceAddrs() + if err != nil { + Log("err in interfaceAddrs : " + err.Error()) + } + for _, a := range interfaceAddresses { + if ipNet, ok := a.(*net.IPNet); ok { + if ipNet.IP.To4() != nil { + certificateRequest.IPAddresses = append(certificateRequest.IPAddresses, ipNet.IP) + } + } + } + + hostPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, nil, nil, err + } + hostPublicKey := &hostPrivateKey.PublicKey + + return certificateRequest, hostPublicKey, hostPrivateKey, nil +} + +// SignCertificate signs a certificate request certificate with a public key and the the CA information. +// Must be PKI to sign a certificate request. +// To check the validity of the signed certificate request, use : openssl s_server -accept 8080 -www -cert yourcert.crt -key yourcert.key -CAfile CAcert.crt +func (s *Shoset) SignCertificate(certificateRequest *x509.Certificate, hostPublicKey *rsa.PublicKey) []byte { + if !s.GetIsPki() { + return nil + } + + filePath := filepath.Join(s.ConnsByLname.GetConfig().GetBaseDirectory(), s.ConnsByLname.GetConfig().GetFileName()) + loadedCAkeys, err := tls.LoadX509KeyPair(filepath.Join(filePath, PATH_CA_CERT), filepath.Join(filePath, PATH_CA_PRIVATE_KEY)) + if err != nil { + s.Logger.Error().Msg("couldn't load keyPair : " + err.Error()) + return nil + } + + parsedCAcert, err := x509.ParseCertificate(loadedCAkeys.Certificate[0]) + if err != nil { + s.Logger.Error().Msg("couldn't parse cert : " + err.Error()) + return nil + } + + signedHostCert, err := x509.CreateCertificate(rand.Reader, certificateRequest, parsedCAcert, hostPublicKey, loadedCAkeys.PrivateKey) + if err != nil { + s.Logger.Error().Msg("couldn't sign certificateRequest : " + err.Error()) + return nil + } + return signedHostCert +} + +// SetUpDoubleWay sets up the tls config once the certificate is signed. +// Sets up Double Way for future secured exchanges. +// Updates Single Way for future exchanges with non-certified Shoset. +func (s *Shoset) SetUpDoubleWay() error { + s.mu.Lock() + defer s.mu.Unlock() + + fileName := s.ConnsByLname.GetConfig().GetFileName() + cfgDirectory := s.ConnsByLname.GetConfig().GetBaseDirectory() + CApath := filepath.Join(cfgDirectory, fileName, PATH_CA_CERT) + + os.Setenv(CERT_FILE_ENVIRONMENT, CApath) + + loadedCAkeys, err := tls.LoadX509KeyPair(filepath.Join(cfgDirectory, fileName, PATH_CERT), filepath.Join(cfgDirectory, fileName, PATH_PRIVATE_KEY)) + if err != nil { + s.Logger.Error().Msg("Unable to Load certificate : " + err.Error()) + return errors.New("Unable to Load certificate : " + err.Error()) + } + + CAcertBytes, err := ioutil.ReadFile(CApath) + if err != nil { + s.Logger.Error().Msg("error read file cacert : " + err.Error()) + return errors.New("error read file cacert : " + err.Error()) + } + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(CAcertBytes) + + s.tlsConfigDoubleWay = &tls.Config{ + Certificates: []tls.Certificate{loadedCAkeys}, + RootCAs: caCertPool, // it is important for windows or it will raise a unknown authority error + ClientCAs: caCertPool, + ClientAuth: tls.RequireAndVerifyClientCert, + InsecureSkipVerify: false, + } + + s.tlsConfigSingleWay = &tls.Config{ + Certificates: []tls.Certificate{loadedCAkeys}, + InsecureSkipVerify: false, + } + return nil +} + +// Certify runs a certification request with the CA. +func (s *Shoset) Certify(bindAddress, remoteAddress string) error { + certRequestConn, err := NewShosetConn(s, remoteAddress, OUT) + if err != nil { + s.Logger.Error().Msg("couldn't create shoset : " + err.Error()) + return err + } + + err = certRequestConn.RunPkiRequest(bindAddress) + if err != nil { + s.Logger.Error().Msg("RunPkiRequest didn't work" + err.Error()) + return err + } + s.Logger.Debug().Msg("shoset certified") + return nil +} diff --git a/pki_test.go b/pki_test.go new file mode 100644 index 0000000..5328c3c --- /dev/null +++ b/pki_test.go @@ -0,0 +1,53 @@ +package shoset + +import ( + "crypto/rsa" + "crypto/x509" + "testing" +) + +var certificateRequest *x509.Certificate +var hostPublicKey *rsa.PublicKey +var hostPrivateKey *rsa.PrivateKey +var err error + +// TestPrepareCertificate verifies if PrepareCertificate() function returns expected certificateRequest, hostPublicKey and hostPrivateKey. +func TestPrepareCertificate(t *testing.T) { + certificateRequest, hostPublicKey, hostPrivateKey, err = PrepareCertificate() + if certificateRequest == nil { + t.Errorf("certificateRequest is not valid") + } + if hostPublicKey == nil { + t.Errorf("hostPublicKey is not valid") + } + if hostPrivateKey == nil { + t.Errorf("hostPrivateKey is not valid") + } + if err != nil { + t.Errorf("unexpected error : %s", err) + } +} + +// TestSignCertificate verifies if SignCertificate() function returns a correct signedCertificate. +func TestSignCertificate(t *testing.T) { + shoset := NewShoset("cl", "cl") // cluster + shoset.InitPKI("127.0.0.1:8001") + + TestPrepareCertificate(t) + + signedCertificate := shoset.SignCertificate(certificateRequest, hostPublicKey) + if signedCertificate == nil { + t.Errorf("TestSignCertificate didn't work") + } +} + +// TestGenerateSecret verifies if GenerateSecret() returns a correct secret. +func TestGenerateSecret(t *testing.T) { + shoset := NewShoset("cl", "cl") // cluster + shoset.InitPKI("127.0.0.1:8002") + + secret := shoset.GenerateSecret(VOID, VOID) + if secret == VOID { + t.Errorf("TestGenerateSecret didn't work") + } +} diff --git a/route.go b/route.go new file mode 100644 index 0000000..c2c4264 --- /dev/null +++ b/route.go @@ -0,0 +1,43 @@ +package shoset + +type Route struct { + neighbour string // Direction to send message + neighborConn *ShosetConn // ShosetConn to the neighbour + nb_steps int // Number of steps to destination logical name + uuid string // UUID of the message that broadcasted this route + timestamp int64 +} + +func NewRoute(neighbour string, c *ShosetConn, nb_steps int, uuid string, timestamp int64) Route { + return Route{ + neighbour: neighbour, + neighborConn: c, + nb_steps: nb_steps, + uuid: uuid, + timestamp: timestamp, + } +} + +// GetNeighbour returns neighbour from Route. +func (r Route) GetNeighbour() string { return r.neighbour } + +// GetneighborConn returns ShosetConn to the neighbour from Route. +func (r Route) GetNeighborConn() *ShosetConn { return r.neighborConn } + +// GetNbSteps returns the number of steps of the Route. +func (r Route) GetNbSteps() int { return r.nb_steps } + +// GetUUID returns the UUID from the routing event that created this Route. +func (r Route) GetUUID() string { return r.uuid } + +// GetTimestamp returns timestamp of the creation of the routing event that created this Route. +func (r Route) GetTimestamp() int64 { return r.timestamp } + +// SetNeighbour sets neighbour from Route. +func (r *Route) SetNeighbour(s string) { r.neighbour = s } + +// SetNeighborConn sets NeighborConn from Route. +func (r *Route) SetNeighborConn(c *ShosetConn) { r.neighborConn = c } + +// SetNbSteps sets nb_steps from Route. +func (r *Route) SetNbSteps(i int) { r.nb_steps = i } diff --git a/script/cert_checker.sh b/script/cert_checker.sh new file mode 100644 index 0000000..46e6c30 --- /dev/null +++ b/script/cert_checker.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +#!!! NOT WORKING !!# +#Problems with connection refused and Adresses already in use. + +# macros +REPERTORY=~/.shoset +LOGFILE=$REPERTORY/cert_checker.txt + +i=0 + +for SHOSET in $REPERTORY/*; do + for CONN in $SHOSET/*; do + if [ -d "${CONN}" ]; then + echo "${CONN}" + + # Different ports for server et client ? + # Connection refused ??? + PORT=$(expr 8080 + $i) + echo + echo + cd $CONN/cert + echo "#### Folder currently checked : $PWD" + + #echo -n "$SHOSET : " >> $LOGFILE ?? + + # run openssl command server and check if it worked + openssl s_server -accept $PORT -www -cert ./cert.crt -key ./privateKey.key -CAfile ./CAcertificate.crt -naccept 1 | grep ACCEPT & #>>$LOGFILE + + # sleep 0.5 + + # run openssl command client and quit when connected + # https://stackoverflow.com/questions/25760596/how-to-terminate-openssl-s-client-after-connection + openssl s_client -connect localhost:$PORT -showcerts -CAfile ./CAcertificate.crt <<<"Q" + + # sleep 0.5 + + # Checks hashes of the keys. + openssl pkey -in ./privateKey.key -pubout -outform pem | sha256sum + openssl x509 -in ./cert.crt -pubkey -noout -outform pem | sha256sum + openssl verify -verbose -CAfile ./CAcertificate.crt ./cert.crt + + echo DONE + i=$(expr $i + 1) + fi + done +done diff --git a/script/shoset_checker.sh b/script/shoset_checker.sh new file mode 100644 index 0000000..3bc0ae7 --- /dev/null +++ b/script/shoset_checker.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# !!! NOT WORKING !!! +# Need updating and test. + +# macros +REPERTORY=~/.shoset +LOGFILE=$REPERTORY/shoset_checker.txt +EXPECTED_FILES_NUMBER=$1 + +number_of_errors=0 + +# allow ctrl+C while running +set -m + +# clean files +echo "" > log.txt +echo "" > log_error.txt + +# test program n times +for i in {1..10} +do + # run command + rm -rf $REPERTORY && timeout 45s go run -race test/test.go 4 >> log.txt 2>&1 && grep ERROR log.txt >> log_error.txt + + # count number of files in the repertory + number_of_files=`find $REPERTORY -type f | wc -l` + + if [ $EXPECTED_FILES_NUMBER -eq $number_of_files ] + then + number_of_errors=`expr $number_of_errors + 0` + else + echo "expected $EXPECTED_FILES_NUMBER, got $number_of_files" + number_of_errors=`expr $number_of_errors + 1` + fi + echo "errors : $number_of_errors" + echo "loop number : $i" + sleep 5 +done + +echo $number_of_errors diff --git a/shoset.go b/shoset.go index 270c6f0..5bfce4e 100644 --- a/shoset.go +++ b/shoset.go @@ -2,351 +2,625 @@ package shoset import ( "crypto/tls" - "errors" "fmt" "net" - "os" + "path/filepath" + "sort" "strings" + "sync" + "time" - "github.com/ditrit/shoset/msg" - "github.com/spf13/viper" -) + uuid "github.com/kjk/betterguid" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" -//terminal -var certPath = "./certs/cert.pem" -var keyPath = "./certs/key.pem" + fileMod "github.com/ditrit/shoset/file" + "github.com/ditrit/shoset/msg" -//debugger -// var certPath = "../certs/cert.pem" -// var keyPath = "../certs/key.pem" + concurentData "github.com/ditrit/shoset/concurent_data" + eventBus "github.com/ditrit/shoset/event_bus" +) -// MessageHandlers interface +// MessageHandlers interface. +// Every handler must implement this interface type MessageHandlers interface { - Handle(*ShosetConn) error - SendConn(*ShosetConn, *msg.Message) - Send(*Shoset, *msg.Message) - Wait(*Shoset, *msg.Iterator, string, int) *msg.Message + Get(c *ShosetConn) (msg.Message, error) + HandleDoubleWay(c *ShosetConn, message msg.Message) error + Send(s *Shoset, m msg.Message) + Wait(s *Shoset, replies *msg.Iterator, args map[string]string, timeout int) *msg.Message } -//Shoset : +// Shoset : smart object based on network socket but with upgraded features type Shoset struct { - Context map[string]interface{} //TOTO + Logger zerolog.Logger // pretty logger + + Context map[string]interface{} // used for gandalf - // id string - ConnsByName *MapSafeMapConn // map[string]map[string]*ShosetConn connexions par nom logique - LnamesByType *MapSafeStrings // for gandalf - LnamesByProtocol *MapSafeStrings + ConnsByLname *MapSyncMap // map[lName]map[remoteAddress]*ShosetConn connections by logical name + LnamesByType *MapSyncMap // map[shosetType]map[lName]bool used for gandalf + LnamesByProtocol *MapSyncMap // map[protocolType]map[lName]bool logical names by protocol type + ConnsSingleBool *sync.Map // map[ipAddress]bool ipAddresses waiting in singleWay to be handled for TLS double way + ConnsSingleConn *sync.Map // map[ipAddress]*ShosetConn ShosetConns waiting in singleWay to be handled for TLS double way + RouteTable *sync.Map // map[lName]*Route Route to another logical name + Library fileMod.FileLibrary // Library of files + FileCommands *fileMod.ExternalCommands // File commands + UUID string // unique identifier of the Shoset - lName string // Nom logique de la shoset - ShosetType string // Type logique de la shoset - bindAddress string // Adresse sur laquelle la shoset est bindée + RoutingEventBus eventBus.EventBus // When a route to a Lname is discovered, sends an event to everyone waiting for a route to this Lname + // topic : discovered Lname - // Dictionnaire des queues de message (par type de message) - Queue map[string]*msg.Queue - Get map[string]func(*ShosetConn) (msg.Message, error) - Handle map[string]func(*ShosetConn, msg.Message) error - Send map[string]func(*Shoset, msg.Message) - Wait map[string]func(*Shoset, *msg.Iterator, map[string]string, int) *msg.Message + baseDirectory string // base directory of the Shoset + bindAddress string // address on which the Shoset is bound + logicalName string // logical name of the Shoset + shosetType string // logical type of the shoset + isValid bool // state of the Shoset - must be done differently in future review + isPki bool // is the Shoset admin of network or not + listener net.Listener // generic network listener for stream-oriented protocols - // configuration TLS - tlsConfig *tls.Config - tlsServerOK bool + tlsConfigSingleWay *tls.Config // mutual authentication between client and server + tlsConfigDoubleWay *tls.Config // client authenticate server - // synchronisation des goroutines - Done chan bool + Queue map[string]*msg.Queue // map for message queueing + Handlers map[string]MessageHandlers // map for message handling - viperConfig *viper.Viper - isValid bool + MessageEventBus eventBus.EventBus // Sends an event to everyone waiting for a message of the type received + // topic : MessageType + + Done chan bool // goroutines synchronization //Not used ? + + LaunchedProtocol concurentData.ConcurentSlice // List of IP addesses a Protocol was initiated with (but not yet finished) + // The shoset is ready for use when the list is empty + // Use WaitForProtocols() to wait for the shoset to be ready. + + mu sync.RWMutex } -/* Accessors */ -func (c Shoset) GetBindAddress() string { return c.bindAddress } -func (c Shoset) GetLogicalName() string { return c.lName } -func (c Shoset) GetShosetType() string { return c.ShosetType } -func (c *Shoset) GetIsValid() bool { return c.isValid } +// GetBindAddress returns bindAddress from Shoset. +func (s *Shoset) GetBindAddress() string { + s.mu.RLock() + defer s.mu.RUnlock() + return s.bindAddress +} -func (c *Shoset) SetBindAddress(bindAddress string) { - if bindAddress != "" { - c.bindAddress = bindAddress - } +// GetLogicalName returns lName from Shoset. +func (s *Shoset) GetLogicalName() string { + s.mu.RLock() + defer s.mu.RUnlock() + return s.logicalName } -func (c *Shoset) SetIsValid(state bool) { - c.isValid = state +// GetShosetType returns shosetType from Shoset. +func (s *Shoset) GetShosetType() string { + s.mu.RLock() + defer s.mu.RUnlock() + return s.shosetType } -/* Constructor */ -func NewShoset(lName, ShosetType string) *Shoset { //l - // Creation - shoset := Shoset{} - - // Initialisation - shoset.Context = make(map[string]interface{}) - - shoset.lName = lName - shoset.ShosetType = ShosetType - shoset.viperConfig = viper.New() - shoset.ConnsByName = NewMapSafeMapConn() - shoset.LnamesByType = NewMapSafeStrings() - shoset.LnamesByProtocol = NewMapSafeStrings() - shoset.ConnsByName.SetViper(shoset.viperConfig) - shoset.isValid = true - - // Dictionnaire des queues de message (par type de message) - shoset.Queue = make(map[string]*msg.Queue) - shoset.Get = make(map[string]func(*ShosetConn) (msg.Message, error)) - shoset.Handle = make(map[string]func(*ShosetConn, msg.Message) error) - shoset.Send = make(map[string]func(*Shoset, msg.Message)) - shoset.Wait = make(map[string]func(*Shoset, *msg.Iterator, map[string]string, int) *msg.Message) - - shoset.Queue["cfglink"] = msg.NewQueue() - shoset.Get["cfglink"] = GetConfigLink - shoset.Handle["cfglink"] = HandleConfigLink - - shoset.Queue["cfgjoin"] = msg.NewQueue() - shoset.Get["cfgjoin"] = GetConfigJoin - shoset.Handle["cfgjoin"] = HandleConfigJoin - - shoset.Queue["cfgbye"] = msg.NewQueue() - shoset.Get["cfgbye"] = GetConfigBye - shoset.Handle["cfgbye"] = HandleConfigBye - - shoset.Queue["evt"] = msg.NewQueue() - shoset.Get["evt"] = GetEvent - shoset.Handle["evt"] = HandleEvent - shoset.Send["evt"] = SendEvent - shoset.Wait["evt"] = WaitEvent - - shoset.Queue["cmd"] = msg.NewQueue() - shoset.Get["cmd"] = GetCommand - shoset.Handle["cmd"] = HandleCommand - shoset.Send["cmd"] = SendCommand - shoset.Wait["cmd"] = WaitCommand +// GetIsValid returns isValid from Shoset. +func (s *Shoset) GetIsValid() bool { + s.mu.RLock() + defer s.mu.RUnlock() + return s.isValid +} - //TODO MOVE TO GANDALF - shoset.Queue["config"] = msg.NewQueue() - shoset.Get["config"] = GetConfig - shoset.Handle["config"] = HandleConfig - shoset.Send["config"] = SendConfig - shoset.Wait["config"] = WaitConfig - - // Configuration TLS //////////////////////////////////// à améliorer - if pathCheck(certPath) && pathCheck(keyPath) { - cert, err := tls.LoadX509KeyPair(certPath, keyPath) - if err != nil { // only client in insecure mode - fmt.Println("! Unable to Load certificate !") - shoset.tlsConfig = &tls.Config{InsecureSkipVerify: true} - shoset.tlsServerOK = false - } else { - shoset.tlsConfig = &tls.Config{ - Certificates: []tls.Certificate{cert}, - InsecureSkipVerify: true, - } - shoset.tlsServerOK = true +// GetIsPki returns isPki from Shoset. +func (s *Shoset) GetIsPki() bool { + s.mu.RLock() + defer s.mu.RUnlock() + return s.isPki +} + +// GetListener returns listener from Shoset. +func (s *Shoset) GetListener() net.Listener { + s.mu.RLock() + defer s.mu.RUnlock() + return s.listener +} + +// GetTlsConfigSingleWay returns tlsConfigSingleWay from Shoset. +func (s *Shoset) GetTlsConfigSingleWay() *tls.Config { + s.mu.RLock() + defer s.mu.RUnlock() + return s.tlsConfigSingleWay +} + +// GetTlsConfigDoubleWay returns tlsConfigDoubleWay from Shoset. +func (s *Shoset) GetTlsConfigDoubleWay() *tls.Config { + s.mu.RLock() + defer s.mu.RUnlock() + return s.tlsConfigDoubleWay +} + +// GetConnsByTypeArray returns an array of *ShosetConn known from a Shoset depending on a specific shosetType. +func (s *Shoset) GetConnsByTypeArray(shosetType string) []*ShosetConn { + s.mu.RLock() + defer s.mu.RUnlock() + lNamesByType := s.LnamesByType.Keys(shosetType) + var connsByType []*ShosetConn + for _, lName := range lNamesByType { + lNameMap, _ := s.ConnsByLname.Load(lName) + keys := Keys(lNameMap.(*sync.Map), ALL) + for _, key := range keys { + conn, _ := lNameMap.(*sync.Map).Load(key) + connsByType = append(connsByType, conn.(*ShosetConn)) } - } else { - fmt.Println("! Unable to Load certificate !") - shoset.tlsServerOK = false } - return &shoset + return connsByType } -// Display with fmt - override the print of the object -func (c Shoset) String() string { - descr := fmt.Sprintf("Shoset - lName: %s,\n\t\tbindAddr : %s,\n\t\ttype : %s, \n\t\tConnsByName : ", c.GetLogicalName(), c.GetBindAddress(), c.GetShosetType()) - for _, lName := range c.ConnsByName.Keys() { - c.ConnsByName.Iterate(lName, - func(key string, val *ShosetConn) { - descr = fmt.Sprintf("%s %s\n\t\t\t ", descr, val) - }) +// IsCertified returns true if path corresponds to an existing repertory which means that the Shoset has created its repertory and is certified. +func (s *Shoset) IsCertified(path string) bool { + if fileExists(filepath.Join(path, PATH_CA_PRIVATE_KEY)) { + s.SetIsPki(true) } - descr = fmt.Sprintf("%s \n\t\tLnamesByProtocol : MapSafeStrings{%s\n\t ", descr, c.LnamesByProtocol) - descr = fmt.Sprintf("%s LnamesByType : MapSafeStrings{%s\n\t ", descr, c.LnamesByType) - // c.LnamesByType.Iterate( - // func(key string, val map[string]bool) { - // descr = fmt.Sprintf("%s %s\n\t\t\t ", descr, val) - // }) - // c.LnamesByProtocol.Iterate( - // func(key string, val map[string]bool) { - // descr = fmt.Sprintf("%s %s\n\t\t\t ", descr, val) - // }) - return descr + return fileExists(path) } -//Bind : Connect to another Shoset -func (c *Shoset) Bind(address string) error { - if c.GetBindAddress() != "" { //socket already bounded to a port (already passed this Bind function once) - fmt.Println("Shoset already bound") - return errors.New("Shoset already bound") - } - if !c.tlsServerOK { // TLS configuration not ok (security problem) - fmt.Println("TLS configuration not OK (certificate not found / loaded)") - return errors.New("TLS configuration not OK (certificate not found / loaded)") - } - ipAddress, err := GetIP(address) // parse the address from function parameter to get the IP - if err != nil { // check if IP is ok - return err +// SetBindAddress sets the bindAddress for a Shoset. +func (s *Shoset) SetBindAddress(bindAddress string) { + s.mu.Lock() + defer s.mu.Unlock() + s.bindAddress = bindAddress +} + +// SetIsValid sets the state for a Shoset. +func (s *Shoset) SetIsValid(state bool) { + s.mu.Lock() + defer s.mu.Unlock() + s.isValid = state +} + +// SetIsPki sets the state for a Shoset. +func (s *Shoset) SetIsPki(state bool) { + s.mu.Lock() + defer s.mu.Unlock() + s.isPki = state +} + +// SetListener sets the listener for a Shoset. +func (s *Shoset) SetListener(listener net.Listener) { + s.mu.Lock() + defer s.mu.Unlock() + s.listener = listener +} + +// DeleteConn deletes a ShosetConn from ConnsByLname map from a Shoset +func (s *Shoset) DeleteConn(Lname, remoteAddress string) { + // Lock shoset for the operation + s.mu.Lock() + defer s.mu.Unlock() + + // Check if the ShosetCon exists + conn, ok := s.ConnsByLname.LoadValueFromKeys(Lname, remoteAddress) + if !ok { + return } + s.Logger.Debug().Msg("Deleting conn Lname : " + Lname + " IP : " + remoteAddress) + + c := conn.(*ShosetConn) + + s.LnamesByProtocol.DeleteValueFromKeys(c.GetProtocol(), Lname) + s.LnamesByType.DeleteValueFromKeys(c.GetRemoteShosetType(), Lname) + s.ConnsByLname.DeleteValueFromKeys(Lname, remoteAddress) - viperAddress := computeAddress(ipAddress) - c.ConnsByName.SetConfigName(viperAddress) + // Deletes from the config file + s.ConnsByLname.cfg.DeleteFromKey(c.GetProtocol(), []string{c.GetRemoteAddress()}) + + // Finds and deletes Routes using the deleted ShosetConn + s.RouteTable.Range( + func(key, val interface{}) bool { + if val.(Route).GetNeighborConn() == c { + c.GetShoset().RouteTable.Delete(key) + } + return true + }) + count := 0 + s.ConnsByLname.Iterate(func(lname string, address string, value interface{}) { + if lname == c.GetLocalLogicalName() { + count++ + } + }) + s.Handlers["file"].(*FileHandler).SetNbConn(count) +} - // viper config - dirname, err := os.UserHomeDir() +// Waits for every Conn initialised to be ready for use +func (s *Shoset) WaitForProtocols(timeout int) { + s.Logger.Debug().Str("lname", s.GetLogicalName()).Msg("Waiting for all Protocol to complete on shoset.") + err := s.LaunchedProtocol.WaitForEmpty(timeout) if err != nil { - fmt.Println(err) + s.Logger.Error().Msg("Failed to establish connection to some adresses (timeout) : " + s.LaunchedProtocol.String()) + } else { + s.Logger.Debug().Str("lname", s.GetLogicalName()).Msg("All Protocols done on shoset.") } - if !pathCheck(dirname + "/.shoset_config/") { - os.Mkdir(dirname+"/.shoset_config/", 0700) +} + +// NewShoset creates a new Shoset object. +// Initializes each fields, queues and handlers. +// logicalName : each node with the same logicalName have the same document and the same configuration (same link with other nodes) +// shosetType : the type of the node (not used for now) +func NewShoset(logicalName, shosetType string) *Shoset { + UUID := uuid.New() + s := Shoset{ + Logger: log.With().Str("uuid", UUID).Logger(), + + Context: make(map[string]interface{}), + + ConnsByLname: new(MapSyncMap), + LnamesByType: new(MapSyncMap), + LnamesByProtocol: new(MapSyncMap), + ConnsSingleBool: new(sync.Map), + ConnsSingleConn: new(sync.Map), + RouteTable: new(sync.Map), + UUID: UUID, + + RoutingEventBus: eventBus.NewEventBus(), + + logicalName: logicalName, + shosetType: shosetType, + isValid: false, + isPki: false, + listener: nil, + + tlsConfigSingleWay: &tls.Config{InsecureSkipVerify: true}, + tlsConfigDoubleWay: nil, + + Queue: make(map[string]*msg.Queue), + Handlers: make(map[string]MessageHandlers), + MessageEventBus: eventBus.NewEventBus(), + + LaunchedProtocol: concurentData.NewConcurentSlice(), } - c.viperConfig.AddConfigPath(dirname + "/.shoset_config/") - c.viperConfig.SetConfigName(viperAddress) - c.viperConfig.SetConfigType("yaml") + s.ConnsByLname.SetConfig(NewConfig(s.logicalName)) - if err := c.viperConfig.ReadInConfig(); err != nil { - } else { - remotesToJoin, remotesToLink := c.ConnsByName.GetConfig() // get all the sockets we need to join - for _, remote := range remotesToJoin { - c.Protocol(remote, "join") - } - for _, remote := range remotesToLink { - c.Protocol(remote, "link") - } + //s.Queue["cfglink"] = msg.NewQueue() // Not neeeded + s.Handlers["cfglink"] = new(ConfigLinkHandler) + + //s.Queue["cfgjoin"] = msg.NewQueue() // Not neeeded + s.Handlers["cfgjoin"] = new(ConfigJoinHandler) + + //s.Queue["cfgbye"] = msg.NewQueue() // Not neeeded + s.Handlers["cfgbye"] = new(ConfigByeHandler) + + s.Queue["evt"] = msg.NewQueue() + s.Handlers["evt"] = new(EventHandler) + + s.Queue["pkievt_TLSdoubleWay"] = msg.NewQueue() + s.Handlers["pkievt_TLSdoubleWay"] = new(PkiEventHandler) + + s.Queue["pkievt_TLSsingleWay"] = msg.NewQueue() + s.Handlers["pkievt_TLSsingleWay"] = new(PkiEventHandler) + + // s.Queue["routingEvent"] = msg.NewQueue() // Not neeeded + s.Handlers["routingEvent"] = new(RoutingEventHandler) + + s.Queue["forwardAck"] = msg.NewQueue() + s.Handlers["forwardAck"] = new(ForwardAcknownledgeHandler) + + s.Queue["simpleMessage"] = msg.NewQueue() + s.Handlers["simpleMessage"] = new(SimpleMessageHandler) + + s.Queue["cmd"] = msg.NewQueue() + s.Handlers["cmd"] = new(CommandHandler) + + s.Queue["file"] = msg.NewQueue() + fileHandler := new(FileHandler) + s.Handlers["file"] = fileHandler + + //TODO MOVE TO GANDALF + s.Queue["config"] = msg.NewQueue() + s.Handlers["config"] = new(ConfigHandler) + + s.FileCommands = fileMod.NewExternalCommands(&fileHandler.FileTransferImpl) + fileHandler.SetExternalCommands(s.FileCommands) + + s.Logger.Debug().Str("lname", logicalName).Msg("shoset created") + return &s +} + +// String returns the formatted string of Shoset object in a pretty indented way. +func (s *Shoset) String() string { + s.mu.RLock() + defer s.mu.RUnlock() + description := fmt.Sprintf("Shoset{\n\t- lName: %s,\n\t- bindAddr : %s,\n\t- type : %s, \n\t- isPki : %t", s.GetLogicalName(), s.GetBindAddress(), s.GetShosetType(), s.GetIsPki()) + + description += ", \n\t- LaunchedProtocol : " + s.LaunchedProtocol.String() + + description += ", \n\t- ConnsByLname : " + connsByName := []*ShosetConn{} + s.ConnsByLname.Iterate( + func(lname string, ipAddress string, val interface{}) { + connsByName = append(connsByName, val.(*ShosetConn)) + }) + sort.Slice(connsByName, func(i, j int) bool { + return connsByName[i].GetRemoteAddress() < connsByName[j].GetRemoteAddress() + }) + for _, connByName := range connsByName { + description += fmt.Sprintf("\n\t\t&%s", connByName) } - c.SetBindAddress(ipAddress) // bound to the port - go c.handleBind() // process runInconn() - return nil + description += "\n\t- LnamesByProtocol:\n\t" + description += s.LnamesByProtocol.String() + description += "\n\t- LnamesByType:\n\t" + description += s.LnamesByType.String() + + description += "\n\t- RouteTable (destination : {neighbor, Conn to Neighbor, distance, uuid, timestamp}):\n\t\t" + routeTable := map[string]Route{} + routeTableKey := []string{} + s.RouteTable.Range( + func(key, val interface{}) bool { + routeTable[key.(string)] = val.(Route) + routeTableKey = append(routeTableKey, key.(string)) + return true + }) + sort.Strings(routeTableKey) + for _, routeKey := range routeTableKey { + description += fmt.Sprintf("\n\t\t%v : %v", routeKey, routeTable[routeKey]) + } + + description += "\n}\n" + return description } -// runBindTo : handler for the socket -func (c *Shoset) handleBind() error { - listener, err := net.Listen("tcp", c.GetBindAddress()) //open a net listener - if err != nil { // check if listener is ok - fmt.Println("Failed to bind:", err.Error()) +// Bind assigns a local protocol address to the Shoset. +// Runs protocol on other Shosets if needed. +func (s *Shoset) Bind(address string) error { + ipAddress, err := GetIP(address) + if err != nil { + s.Logger.Error().Msg("couldn't set bindAddress : " + err.Error()) return err } - // defer WriteViper() - defer listener.Close() + s.SetBindAddress(ipAddress) - for { - if !c.GetIsValid() { // sockets are not from the same type or don't have the same name / conn ended - return errors.New("error : Invalid connection for join - not the same type/name or shosetConn ended") + err = s.ConnsByLname.GetConfig().ReadConfig(s.ConnsByLname.GetConfig().GetFileName()) + if err == nil { + for _, remote := range s.ConnsByLname.cfg.GetSlice(PROTOCOL_JOIN) { + s.Protocol(address, remote, PROTOCOL_JOIN) } - unencConn, err := listener.Accept() - if err != nil { - fmt.Printf("serverShoset accept error: %s", err) - break + for _, remote := range s.ConnsByLname.cfg.GetSlice(PROTOCOL_LINK) { + s.Protocol(address, remote, PROTOCOL_LINK) } - tlsConn := tls.Server(unencConn, c.tlsConfig) // create the securised connection protocol - address := tlsConn.RemoteAddr().String() - conn, _ := NewShosetConn(c, address, "in") // create the securised connection - conn.socket = tlsConn //we override socket attribut with our securised protocol - go conn.runInConn() } + + listener, err := net.Listen(CONNECTION_TYPE, DEFAULT_IP+strings.Split(ipAddress, ":")[1]) + if err != nil { + s.Logger.Error().Msg("listen error : " + err.Error()) + return err + } + s.SetListener(listener) + + go s.handleBind() return nil } -func (c *Shoset) Protocol(address, protocolType string) (*ShosetConn, error) { - var conn *ShosetConn - switch protocolType { - case "join": - conns := c.ConnsByName.Get(c.GetLogicalName()) - if conns != nil { - exists := conns.Get(address) // check if address is already in the map - if exists != nil { //connection already established for this socket - return exists, nil +// handleBind listens on a specific port every occurring connection. +// Runs tls protocol to establish secured connection. +func (s *Shoset) handleBind() { + defer s.GetListener().Close() + for { + acceptedConn, err := s.GetListener().Accept() + if err != nil { + s.Logger.Error().Msg("serverShoset accept error : " + err.Error()) + return + } + + if exists, _ := s.ConnsSingleBool.Load(strings.Split(acceptedConn.RemoteAddr().String(), ":")[0]); exists != nil { // if the connection is single way + tlsConnSingleWay := tls.Server(acceptedConn, s.tlsConfigSingleWay) + singleWayConn, _ := NewShosetConn(s, acceptedConn.RemoteAddr().String(), IN) + singleWayConn.UpdateConn(tlsConnSingleWay) + go singleWayConn.RunInConnSingle(strings.Split(acceptedConn.RemoteAddr().String(), ":")[0]) + } else { + tlsConnDoubleWay := tls.Server(acceptedConn, s.GetTlsConfigDoubleWay()) + doubleWayConn, _ := NewShosetConn(s, acceptedConn.RemoteAddr().String(), IN) + doubleWayConn.UpdateConn(tlsConnDoubleWay) + + _, err = doubleWayConn.GetConn().Write([]byte(TLS_DOUBLE_WAY_TEST_WRITE + "\n")) // Crashes when launching 2 shosets at the same time + if err == nil { + go doubleWayConn.RunInConnDouble() + } else { + s.ConnsSingleBool.Store(strings.Split(acceptedConn.RemoteAddr().String(), ":")[0], true) } } - if address == c.GetBindAddress() { // connection impossible with itself - return nil, nil + } +} + +// Protocol runs the suitable protocol handler. +// Inits certification if Shoset is not certified yet. +// Binds Shoset to the bindAddress if not bound yet. +func (s *Shoset) Protocol(bindAddress, remoteAddress, protocolType string) { + s.Logger.Debug().Strs("params", []string{bindAddress, remoteAddress, protocolType}).Msg("protocol init") + + ipAddress, err := GetIP(bindAddress) + if err != nil { + s.Logger.Error().Msg("wrong IP format : " + err.Error()) + return + } + formattedIpAddress := strings.Replace(ipAddress, ":", "_", -1) + formattedIpAddress = strings.Replace(formattedIpAddress, ".", "-", -1) // formats for filesystem to 127-0-0-1_8001 instead of 127.0.0.1:8001 + + s.mu.Lock() + s.ConnsByLname.GetConfig().SetFileName(formattedIpAddress) + s.mu.Unlock() + + if !s.IsCertified(filepath.Join(s.ConnsByLname.GetConfig().baseDirectory, formattedIpAddress)) { + s.Logger.Debug().Msg("ask certification") + + _, err = s.ConnsByLname.GetConfig().InitFolders(formattedIpAddress) + if err != nil { + s.Logger.Error().Msg("couldn't init folder: " + err.Error()) + return } - conn, _ := NewShosetConn(c, address, "out") - go conn.runJoinConn() - case "link": - conns := c.ConnsByName.Get(c.GetLogicalName()) - if conns != nil { - exists := conns.Get(address) // check if address is already in the map - if exists != nil { //connection already established for this socket - return exists, nil - } + err = s.Certify(bindAddress, remoteAddress) + if err != nil { + return } - if address == c.GetBindAddress() { // connection impossible with itself - return nil, nil + } else { + // Loads certificats from the folder + err = s.SetUpDoubleWay() + if err != nil { + s.Logger.Error().Msg(err.Error()) + return } - conn, _ := NewShosetConn(c, address, "out") - go conn.runOutConn() - case "bye": - conn, _ := NewShosetConn(c, address, "out") - go conn.runEndConn() - default: - fmt.Println("Wrong input protocolType") - return nil, errors.New("wrong input protocolType") } - return conn, nil -} -func (c *Shoset) deleteConn(connAddr, connLname string) { - // fmt.Println(c.GetBindAddress(), " enter deleteConn") - if conns := c.ConnsByName.Get(connLname); conns != nil { - if conns.Get(connAddr) != nil { - // fmt.Println(c.GetBindAddress(), " is ok in deleteConn") - c.ConnsByName.Delete(connLname, connAddr) + if s.GetBindAddress() == VOID { + err := s.Bind(bindAddress) + if err != nil { + s.Logger.Error().Msg("couldn't set bindAddress : " + err.Error()) + return } } -} -// compute the name for the .yaml file corresponding to each socket -func computeAddress(ipAddress string) string { - _ipAddress := strings.Replace(ipAddress, ":", "_", 1) - _ipAddress = strings.Replace(_ipAddress, ".", "~", 3) - name := "shoset_" + _ipAddress - return name + if IP, _ := GetIP(remoteAddress); IP == s.GetBindAddress() { + s.Logger.Error().Msg("can't protocol on itself : " + IP) + return + } + + if remoteAddress != VOID { + protocolConn, _ := NewShosetConn(s, remoteAddress, OUT) + cfg := msg.NewConfigProtocol(s.GetBindAddress(), s.GetLogicalName(), s.GetShosetType(), protocolType) + + s.LaunchedProtocol.AppendToConcurentSlice(protocolConn.GetRemoteAddress()) // Adds remote adress to the list of initiated but not ready connexion adresses + go protocolConn.HandleConfig(cfg) + } } -// returns bool whether the given file or directory exists -func pathCheck(path string) bool { - _, err := os.Stat(path) - if os.IsNotExist(err) { - fmt.Println("File does not exist.") - return false +func (s *Shoset) EndProtocol(Lname, remoteAddress string) { + // Finds the ShosetConn in the list + var c *ShosetConn + if conn, ok := s.ConnsByLname.LoadValueFromKeys(Lname, remoteAddress); !ok { + s.Logger.Error().Msg("No Existing connection to Lname : " + Lname + " IP : " + remoteAddress + ", no connection to end.") + return + } else { + c = conn.(*ShosetConn) } - return true + + cfg := msg.NewConfigProtocol(s.GetBindAddress(), s.GetLogicalName(), s.GetShosetType(), DELETE) + + err := c.GetWriter().SendMessage(*cfg) + if err != nil { + c.Logger.Error().Msg("couldn't send cfg: " + err.Error()) + return + //Retry ? Acknowledge ? + } + + s.LnamesByProtocol.AppendToKeys(PROTOCOL_EXIT, Lname, true) //Bye ou delete ? + s.DeleteConn(Lname, remoteAddress) } -func (c *Shoset) GetConnsByType(shosetType string) map[string]*ShosetConn { - lNames := c.LnamesByType.Keys(shosetType) - connsByType := make(map[string]*ShosetConn) - for _, lName := range lNames { - lNameMap := c.ConnsByName.Get(lName) - keys := lNameMap.Keys("all") - for _, key := range keys { - connsByType[key] = lNameMap.Get(key) +// ######## Route and forwarding : ######## + +// Forward messages destined to another Lname to the next step on the Route +func (s *Shoset) forwardMessage(m msg.Message) { + masterTimeout := time.NewTimer(time.Duration(MASTER_SEND_TIMEOUT) * time.Second) + tryNumber := 0 + + for { + if route, ok := s.RouteTable.Load(m.GetDestinationLname()); ok { // There is a known Route to the destination Lname + s.Logger.Debug().Msg(s.GetLogicalName() + " is sending a message to " + m.GetDestinationLname() + " through " + route.(Route).GetNeighbour() + " IP : " + route.(Route).GetNeighborConn().GetRemoteAddress() + " .") + + // Forward message + err := route.(Route).GetNeighborConn().GetWriter().SendMessage(m) + if err != nil { + s.Logger.Error().Msg("Couldn't send forwarded message : " + err.Error()) + } + + // Wait for Acknowledge + forwardAck := s.Wait("forwardAck", map[string]string{"UUID": m.GetUUID()}, TIMEOUT_ACK, nil) + if forwardAck == nil { + s.Logger.Warn().Msg("Forward message : Failed to forward message destined to " + m.GetDestinationLname() + " Forward Acknowledge not received. (retrying)") + + // Invalidate route + s.RouteTable.Delete(m.GetDestinationLname()) + + // Reroute network + routing := msg.NewRoutingEvent(s.GetLogicalName(), true, 0, "") + s.Send(routing) + + tryNumber++ + if tryNumber > MAX_FORWARD_TRY { + s.Logger.Warn().Msg("Forward message : Failed to forward message destined to " + m.GetDestinationLname() + ". Max number of attempts exceeded. (Giving up)") + return + } else { + continue + } + } + return + + } else { // There is no known Route to the destination Lname -> Wait for one to be available + s.Logger.Warn().Msg("Forward message : Failed to forward message destined to " + m.GetDestinationLname() + ". (no route) (waiting for correct route)") + + // Reroute network + routing := msg.NewRoutingEvent(s.GetLogicalName(), true, 0, "") + s.Send(routing) + + // Creation channel + chRoute := make(chan interface{}) + s.RoutingEventBus.Subscribe(m.GetDestinationLname(), chRoute) + + // Check if the route did not become available while you were preparing the channel + _, ok := s.RouteTable.Load(m.GetDestinationLname()) + if ok { + continue + } + select { + case <-chRoute: + s.RoutingEventBus.UnSubscribe(m.GetDestinationLname(), chRoute) + break + case <-masterTimeout.C: + s.Logger.Error().Msg("Couldn't send forwarded message : " + "Timed out before correct route discovery. (Waited to long for the route)") + return + } } } - return connsByType } -func (c *Shoset) GetConnsByTypeArray(shosetType string) []*ShosetConn { - lNames := c.LnamesByType.Keys(shosetType) - // fmt.Println("lNames : ", lNames) - var connsByType []*ShosetConn - for _, lName := range lNames { - lNameMap := c.ConnsByName.Get(lName) - keys := lNameMap.Keys("all") - for _, key := range keys { - connsByType = append(connsByType, lNameMap.Get(key)) - } +func (s *Shoset) SaveRoute(c *ShosetConn, routingEvt *msg.RoutingEvent) { + s.Logger.Debug().Msg(s.GetLogicalName() + " is saving a route to " + routingEvt.GetOrigin() + " through " + c.GetRemoteLogicalName() + " IP : " + c.GetRemoteAddress()) + + s.RouteTable.Store(routingEvt.GetOrigin(), NewRoute(c.GetRemoteLogicalName(), c, routingEvt.GetNbSteps(), routingEvt.GetUUID(), routingEvt.GetRerouteTimestamp())) + + // Send NewRouteEvent + s.RoutingEventBus.Publish(routingEvt.GetOrigin(), true) + + // Reroutes self to try to take advantage of the change in the network that triggered the Save but with the same timestamp and ID to avoid triggering it over and over. + reRouting := msg.NewRoutingEvent(c.GetLocalLogicalName(), false, routingEvt.GetRerouteTimestamp(), routingEvt.GetUUID()) + s.Send(reRouting) + + // Rebroadcast Routing event + routingEvt.SetNbSteps(routingEvt.GetNbSteps() + 1) + s.Send(routingEvt) +} + +// ######## Send and Receice Messages : ######## + +// Finds the correct send function for the type of message using the handler and call it. +func (s *Shoset) Send(msg msg.Message) { + if msgType := msg.GetMessageType(); contains(SENDABLE_TYPES, msgType) { + s.Handlers[msgType].Send(s, msg) + } else { + s.Logger.Error().Msg("Trying to send an invalid message type or message of a type without a Send function. Message type : " + msgType) } - return connsByType } -func contains(s []string, str string) bool { - for _, v := range s { - if v == str { - return true - } +// Wait for message +// args for event("evt") type : map[string]string{"topic": "topic_name", "event": "event_name"} +// Leave iterator at nil if you don't want to supply it yourself. (avoids reading multiple time the same message) +func (s *Shoset) Wait(msgType string, args map[string]string, timeout int, iterator *msg.Iterator) msg.Message { + if !contains(RECEIVABLE_TYPES, msgType) { + s.Logger.Error().Msg("Trying to receive an invalid message type or message of a type without a Wait function. Message type : " + msgType) + return nil } - return false -} \ No newline at end of file + + if iterator == nil { + iterator = msg.NewIterator(s.Queue[msgType]) + } + + event := s.Handlers[msgType].Wait(s, iterator, args, timeout) + + if event == nil { + return nil + } else { + return *(event) + } +} diff --git a/shosetConn.go b/shosetConn.go index 3a5f96e..cab00c9 100644 --- a/shosetConn.go +++ b/shosetConn.go @@ -5,307 +5,444 @@ import ( "errors" "fmt" "io" + "net" + "unsafe" + "strings" + "sync" + "syscall" "time" - // uuid "github.com/kjk/betterguid" - "github.com/ditrit/shoset/msg" + + uuid "github.com/kjk/betterguid" + "github.com/rs/zerolog" ) -// ShosetConn : client connection +// ShosetConn : secured connection based on tls.Conn but with upgraded features type ShosetConn struct { - socket *tls.Conn - remoteLname string // logical name of the socket in fornt of this one - remoteShosetType string // shosetType of the socket in fornt of this one - dir string - remoteAddress string // addresse of the socket in fornt of this one - ch *Shoset - rb *msg.Reader - wb *msg.Writer - isValid bool // for join protocol + Logger zerolog.Logger // pretty logger + + conn *tls.Conn // secured connection between client and server + + shoset *Shoset // network socket but with upgraded features + + rb *msg.Reader // reader safe for goroutines + wb *msg.Writer // writer safe for goroutines + + remoteLname string // logical name of the socket in front of this one + remoteShosetType string // shosetType of the socket in front of this one + direction string // direction of the connection (in or out) + remoteAddress string // address of the socket in front of this one + + protocol string // protocol type used by the ShosetConn (join, link, ...) (Usualy is not known ("") at the time of creation of the ShosetConn.) + + isValid bool // status of the ShosetConn + + mu sync.RWMutex } -// GetDir : -func (c *ShosetConn) GetDir() string { return c.dir } +// GetConn returns conn from ShosetConn. +func (c *ShosetConn) GetConn() *tls.Conn { + c.mu.RLock() + defer c.mu.RUnlock() + return c.conn +} -// GetCh : -func (c *ShosetConn) GetCh() *Shoset { return c.ch } +// GetShoset returns shoset from ShosetConn. +func (c *ShosetConn) GetShoset() *Shoset { + c.mu.RLock() + defer c.mu.RUnlock() + return c.shoset +} -func (c *ShosetConn) GetLocalLogicalName() string { return c.ch.GetLogicalName() } +// GetReader returns rb from ShosetConn. +func (c *ShosetConn) GetReader() *msg.Reader { + c.mu.RLock() + defer c.mu.RUnlock() + return c.rb +} -// GetName : // remote logical Name -func (c *ShosetConn) GetRemoteLogicalName() string { return c.remoteLname } +// GetWriter returns wb from ShosetConn. +func (c *ShosetConn) GetWriter() *msg.Writer { + c.mu.RLock() + defer c.mu.RUnlock() + return c.wb +} -func (c *ShosetConn) GetLocalShosetType() string { return c.ch.GetShosetType() } +// GetRemoteLogicalName returns remoteLname from ShosetConn. +func (c *ShosetConn) GetRemoteLogicalName() string { + c.mu.RLock() + defer c.mu.RUnlock() + return c.remoteLname +} -// GetShosetType : // remote ShosetTypeName -func (c *ShosetConn) GetRemoteShosetType() string { return c.remoteShosetType } +// GetLocalLogicalName returns shoset.GetLogicalName() from ShosetConn. +func (c *ShosetConn) GetLocalLogicalName() string { + c.mu.RLock() + defer c.mu.RUnlock() + return c.GetShoset().GetLogicalName() +} + +// GetRemoteShosetType returns remoteShosetType from ShosetConn. +func (c *ShosetConn) GetRemoteShosetType() string { + c.mu.RLock() + defer c.mu.RUnlock() + return c.remoteShosetType +} + +// GetLocalShosetType returns shoset.GetShosetType() from ShosetConn. +func (c *ShosetConn) GetLocalShosetType() string { + c.mu.RLock() + defer c.mu.RUnlock() + return c.GetShoset().GetShosetType() +} + +// GetDirection returns direction from ShosetConn. +func (c *ShosetConn) GetDirection() string { + c.mu.RLock() + defer c.mu.RUnlock() + return c.direction +} -// GetBindAddr : port sur lequel on est bindé -func (c *ShosetConn) GetLocalAddress() string { return c.ch.GetBindAddress() } +// GetRemoteAddress returns remoteAddress from ShosetConn. +func (c *ShosetConn) GetRemoteAddress() string { + c.mu.RLock() + defer c.mu.RUnlock() + return c.remoteAddress +} + +// GetProtocol returns protocol from ShosetConn. +func (c *ShosetConn) GetProtocol() string { + c.mu.RLock() + defer c.mu.RUnlock() + return c.protocol +} + +// GetLocalAddress returns shoset.GetBindAddress() from ShosetConn. +func (c *ShosetConn) GetLocalAddress() string { + c.mu.RLock() + defer c.mu.RUnlock() + return c.GetShoset().GetBindAddress() +} + +// GetIsValid returns isValid from ShosetConn. +func (c *ShosetConn) GetIsValid() bool { + c.mu.RLock() + defer c.mu.RUnlock() + return c.isValid +} -func (c *ShosetConn) GetRemoteAddress() string { return c.remoteAddress } +// SetConn sets the lName for a ShosetConn. +func (c *ShosetConn) SetConn(conn *tls.Conn) { + c.mu.Lock() + defer c.mu.Unlock() + c.conn = conn +} -func (c *ShosetConn) GetIsValid() bool { return c.isValid } +// UpdateConn updates conn attribute along with its reader and writer. +func (c *ShosetConn) UpdateConn(conn *tls.Conn) { + c.SetConn(conn) + c.GetReader().UpdateReader(conn) + c.GetWriter().UpdateWriter(conn) +} -// SetName : // remote logical Name -func (c *ShosetConn) SetRemoteLogicalName(lName string) { // remote logical Name - c.remoteLname = lName // remote logical Name - // c.GetCh().ConnsByName.Set(c.GetName(), c.GetRemoteAddress(), c) +// SetRemoteLogicalName sets the lName for a ShosetConn. +func (c *ShosetConn) SetRemoteLogicalName(lName string) { + c.mu.Lock() + defer c.mu.Unlock() + c.remoteLname = lName } -// SetBindAddr : +// SetLocalAddress sets the bindAddress for a ShosetConn. func (c *ShosetConn) SetLocalAddress(bindAddress string) { - if bindAddress != "" { - c.ch.SetBindAddress(bindAddress) - } + c.mu.Lock() + defer c.mu.Unlock() + c.GetShoset().SetBindAddress(bindAddress) } -// SetShosetType : // remote ShosetType +// SetRemoteShosetType sets the ShosetType for a ShosetConn. func (c *ShosetConn) SetRemoteShosetType(ShosetType string) { - if ShosetType != "" { - c.remoteShosetType = ShosetType - } + c.mu.Lock() + defer c.mu.Unlock() + c.remoteShosetType = ShosetType } +// SetProtocol sets the protocol for a ShosetConn. +func (c *ShosetConn) SetProtocol(protocol string) { + c.mu.Lock() + defer c.mu.Unlock() + c.protocol = protocol +} + +// SetIsValid sets the state for a ShosetConn. func (c *ShosetConn) SetIsValid(state bool) { + c.mu.Lock() + defer c.mu.Unlock() c.isValid = state } +// SetRemoteAddress sets the address for a ShosetConn. func (c *ShosetConn) SetRemoteAddress(address string) { - if address != "" { - c.remoteAddress = address - } + c.mu.Lock() + defer c.mu.Unlock() + c.remoteAddress = address } -func NewShosetConn(c *Shoset, address string, dir string) (*ShosetConn, error) { - // Creation - conn := ShosetConn{} - // Initialisation attributs ShosetConn - conn.ch = c - conn.dir = dir - conn.socket = new(tls.Conn) - conn.rb = new(msg.Reader) - conn.wb = new(msg.Writer) - ipAddress, err := GetIP(address) - if err != nil { - return nil, err - } - conn.remoteAddress = ipAddress - conn.isValid = true - return &conn, nil -} +// Stores stores info about ShosetConn and Shoset for protocols +func (c *ShosetConn) Store(protocol, lName, address, shosetType string) { + c.SetProtocol(protocol) -func (c *ShosetConn) String() string { - return fmt.Sprintf("ShosetConn{ name : %s, type : %s, way : %s, remoteAddress : %s}", c.GetRemoteLogicalName(), c.GetRemoteShosetType(), c.GetDir(), c.GetRemoteAddress()) -} + c.SetRemoteLogicalName(lName) + c.SetRemoteShosetType(shosetType) -// ReadString : -func (c *ShosetConn) ReadString() (string, error) { - return c.rb.ReadString() -} + c.GetShoset().LnamesByProtocol.AppendToKeys(protocol, lName, true) + c.GetShoset().LnamesByType.AppendToKeys(shosetType, lName, true) + c.GetShoset().ConnsByLname.StoreConfig(lName, address, protocol, c) -// ReadMessage : -func (c *ShosetConn) ReadMessage(data interface{}) error { - return c.rb.ReadMessage(data) -} + // Reroute the network + c.Logger.Debug().Msg("rerouting the network by sending new routing event") + routing := msg.NewRoutingEvent(c.GetLocalLogicalName(), true, 0, "") + c.GetShoset().Send(routing) -// WriteString : -func (c *ShosetConn) WriteString(data string) (int, error) { - return c.wb.WriteString(data) + c.SetIsValid(true) + c.GetShoset().SyncLibrary(c) + count := 0 + c.GetShoset().ConnsByLname.Iterate(func(lname string, address string, value interface{}) { + if lname == c.GetLocalLogicalName() { + count++ + } + }) + c.GetShoset().Handlers["file"].(*FileHandler).SetNbConn(count) } -// Flush : -func (c *ShosetConn) Flush() error { - return c.wb.Flush() +// NewShosetConn creates a new ShosetConn object for a specific address. +// Initializes each fields. +func NewShosetConn(s *Shoset, address, direction string) (*ShosetConn, error) { + ipAddress, err := GetIP(address) + if err != nil { + return nil, err + } + + logger := s.Logger.With().Str("scid", uuid.New()).Logger() + logger.Debug().Strs("address-direction", []string{address, direction}).Msg("shosetConn created") + + return &ShosetConn{ + Logger: logger, + shoset: s, + direction: direction, + conn: new(tls.Conn), + rb: new(msg.Reader), + wb: new(msg.Writer), + remoteAddress: ipAddress, + isValid: false, + }, nil } -// WriteMessage : -func (c *ShosetConn) WriteMessage(data interface{}) error { - return c.wb.WriteMessage(data) +// String returns the formatted string of ShosetConn object in a pretty way. +func (c *ShosetConn) String() string { + return fmt.Sprintf("ShosetConn{RemoteLogicalName : %s, remoteAddress : %s, type : %s, protocol : %s, way : %s, isValid : %v}", c.GetRemoteLogicalName(), c.GetRemoteAddress(), c.GetRemoteShosetType(), c.GetProtocol(), c.GetDirection(), c.GetIsValid()) } -// RunOutConn : handler for the socket, for Link() -func (c *ShosetConn) runOutConn() { - myConfig := msg.NewCfg(c.ch.bindAddress, c.ch.lName, c.ch.ShosetType, "link") +// HandleConfig handles ConfigProtocol message. +// Connects to the remote address and sends the protocol through this connection. +func (c *ShosetConn) HandleConfig(cfg *msg.ConfigProtocol) { + defer func() { + c.Logger.Debug().Msg("HandleConfig: socket closed") + c.GetConn().Close() + }() for { - if !c.GetIsValid() { // sockets are not from the same type or don't have the same name / conn ended - break + protocolConn, err := tls.Dial(CONNECTION_TYPE, c.GetRemoteAddress(), c.GetShoset().GetTlsConfigDoubleWay()) + if err != nil { + time.Sleep(time.Millisecond * time.Duration(1000)) + c.Logger.Error().Msg("HandleConfig err: " + err.Error()) + continue } - conn, err := tls.Dial("tcp", c.GetRemoteAddress(), c.ch.tlsConfig) + c.UpdateConn(protocolConn) + + err = c.GetWriter().SendMessage(*cfg) if err != nil { - time.Sleep(time.Millisecond * time.Duration(100)) + c.Logger.Error().Msg("couldn't send cfg: " + err.Error()) continue - } else { - c.socket = conn - c.rb = msg.NewReader(c.socket) - c.wb = msg.NewWriter(c.socket) - defer conn.Close() - - // receive messages - for { - if c.GetRemoteLogicalName() == "" { - c.SendMessage(*myConfig) - } - - err := c.receiveMsg() - time.Sleep(time.Millisecond * time.Duration(100)) - if err != nil { - c.SetRemoteLogicalName("") // reinitialize conn - break - } + } + + for { + err := c.ReceiveMessage() + if err != nil { + c.Logger.Error().Msg("socket closed: err in ReceiveMessage HandleConfig: " + err.Error()) + break } } } } -// RunJoinConn : handler for the socket, for Join() -func (c *ShosetConn) runJoinConn() { - joinConfig := msg.NewCfg(c.ch.bindAddress, c.ch.lName, c.ch.ShosetType, "join") //we create a new message config +// RunInConnSingle runs ReceiveMessage for TLS Single Way connection. +func (c *ShosetConn) RunInConnSingle(address string) { + c.GetShoset().ConnsSingleBool.Delete(address) + //afficher le que la connexion est single + c.Logger.Debug().Msg("single_way") + err := c.ReceiveMessage() + if err != nil { + c.Logger.Error().Msg("socket closed: err in ReceiveMessage RunInConnSingle: " + err.Error()) + return + } +} + +// RunInConnDouble runs ReceiveMessage for TLS Double Way connection. +func (c *ShosetConn) RunInConnDouble() { + c.Logger.Debug().Msg("double_way") + defer func() { + c.Logger.Debug().Msg("double_way: socket closed") + c.GetConn().Close() + }() + for { - if !c.GetIsValid() { // sockets are not from the same type or don't have the same name / conn ended - break + err := c.ReceiveMessage() + if err != nil { + c.Logger.Error().Msg("err in ReceiveMessage RunInConnDouble: " + err.Error()) + return } - conn, err := tls.Dial("tcp", c.GetRemoteAddress(), c.ch.tlsConfig) // we wait for a socket to connect each loop + } +} - if err != nil { // no connection occured - time.Sleep(time.Millisecond * time.Duration(100)) - continue - } else { // a connection occured - c.socket = conn - c.rb = msg.NewReader(c.socket) - c.wb = msg.NewWriter(c.socket) - defer conn.Close() - - // receive messages - for { - if c.GetRemoteLogicalName() == "" { - c.SendMessage(*joinConfig) - } - - err := c.receiveMsg() - time.Sleep(time.Millisecond * time.Duration(100)) - if err != nil { - c.SetRemoteLogicalName("") // reinitialize conn - break - } - } +// ReceiveMessage read incoming message type and runs handleMessageType to handle it. +func (c *ShosetConn) ReceiveMessage() error { + messageType, err := c.GetReader().ReadString() + switch { + case err == io.EOF: + c.GetShoset().DeleteConn(c.GetRemoteLogicalName(), c.GetRemoteAddress()) + return err + case errors.Is(err, syscall.ECONNRESET): + return nil + case errors.Is(err, syscall.EPIPE): + return nil + case err != nil: + if c.GetDirection() == IN { + c.GetShoset().DeleteConn(c.GetRemoteLogicalName(), c.GetRemoteAddress()) } + return err } + messageType = strings.Trim(messageType, "\n") + + if messageType == TLS_DOUBLE_WAY_TEST_WRITE { // do not handle this message, test for shoset.handleBind() + return nil + } + + err = c.handleMessageType(messageType) + if err != nil { + return err + } + return nil } -// runEndConn : handler for the socket, for Bye() -func (c *ShosetConn) runEndConn() { - // fmt.Println(c.ch.GetBindAddress(), "enter run endconn") - byeConfig := msg.NewCfg(c.ch.bindAddress, c.ch.lName, c.ch.ShosetType, "bye") //we create a new message config - for { - if !c.GetIsValid() { // sockets are not from the same type or don't have the same name / conn ended - break +// handleMessageType deduce handler from messageType and use it adequately. +func (c *ShosetConn) handleMessageType(messageType string) error { + if messageType != "file" { // because file is too verbose + c.Logger.Debug().Msg("received : " + messageType) + } + handler, ok := c.GetShoset().Handlers[messageType] + if !ok { + if c.GetDirection() == IN { + c.GetShoset().DeleteConn(c.GetRemoteLogicalName(), c.GetRemoteAddress()) } + return errors.New("ReceiveMessage : non implemented type of message " + messageType) + } - // fmt.Println(c.ch.GetBindAddress(), "in run endconn") - conn, err := tls.Dial("tcp", c.GetRemoteAddress(), c.ch.tlsConfig) // we wait for a socket to connect each loop - - if err != nil { // no connection occured - time.Sleep(time.Millisecond * time.Duration(100)) - continue - } else { // a connection occured - c.socket = conn - c.rb = msg.NewReader(c.socket) - c.wb = msg.NewWriter(c.socket) - defer conn.Close() - - // receive messages - for { - if c.GetRemoteLogicalName() == "" { - c.SendMessage(*byeConfig) - } - - err := c.receiveMsg() - time.Sleep(time.Millisecond * time.Duration(100)) - if err != nil { - c.SetRemoteLogicalName("") // reinitialize conn - break - } - } + messageValue, err := handler.Get(c) + if err != nil { + if c.GetDirection() == IN { + c.GetShoset().DeleteConn(c.GetRemoteLogicalName(), c.GetRemoteAddress()) } + return errors.New("ReceiveMessage : can not read value of " + messageType + " : " + err.Error()) } -} -// runInConn : handler for the connection, for handleBind() -func (c *ShosetConn) runInConn() { - c.rb = msg.NewReader(c.socket) - c.wb = msg.NewWriter(c.socket) - defer c.socket.Close() + // If the message is of a forwardable type, an Acknowledge is expected by the sender + if contains(FORWARDABLE_TYPES, messageType) { + // Send back FowarkAck + forwardAck := msg.NewForwardAck(messageValue.GetUUID(), messageValue.GetTimestamp()) + err := c.GetWriter().SendMessage(forwardAck) - // receive messages - for { - err := c.receiveMsg() - time.Sleep(time.Millisecond * time.Duration(10)) if err != nil { - if err.Error() == "error : Invalid connection for join - not the same type/name or shosetConn ended" { - c.ch.SetIsValid(false) - goto Exit - } - break + c.Logger.Error().Msg("Couldn't send FowarkAck message : " + err.Error()) } } -Exit: -} - -// SendMessage : -func (c *ShosetConn) SendMessage(msg msg.Message) { - c.WriteString(msg.GetMsgType()) - c.WriteMessage(msg) -} -func (c *ShosetConn) receiveMsg() error { - if !c.GetIsValid() { - c.ch.deleteConn(c.GetRemoteAddress(), c.GetRemoteLogicalName()) - return errors.New("error : Invalid connection for join - not the same type/name or shosetConn ended") + // Check if the destinationLname is the current Lname + if (messageValue.GetDestinationLname() != c.GetLocalLogicalName()) && messageValue.GetDestinationLname() != "" { + c.GetShoset().forwardMessage(messageValue) + return nil } - // read message type - msgType, err := c.rb.ReadString() switch { - case err == io.EOF: - if c.GetDir() == "in" { - c.ch.deleteConn(c.GetRemoteAddress(), c.GetRemoteLogicalName()) + case messageType == TLS_SINGLE_WAY_PKI_EVT: + err := c.HandleSingleWay(messageValue) + if err != nil { + return err } - return errors.New("receiveMsg : reached EOF - close this connection") - case err != nil: - if c.GetDir() == "in" { - c.ch.deleteConn(c.GetRemoteAddress(), c.GetRemoteLogicalName()) + case contains(MESSAGE_TYPES, messageType): + err := handler.HandleDoubleWay(c, messageValue) + if err != nil { + return err } - return errors.New("error : receiveMsg : failed to read - close this connection") + default: + c.Logger.Error().Msg("wrong messageType : " + messageType) + return errors.New("wrong messageType : " + messageType) } - msgType = strings.Trim(msgType, "\n") - // read Message Value - fGet, ok := c.ch.Get[msgType] - if ok { - msgVal, err := fGet(c) - if err == nil { - // read message data and handle it with the proper function - fHandle, ok := c.ch.Handle[msgType] - if ok { - go fHandle(c, msgVal) //HandleConfigJoin() or HandleConfigLink() or HandleConfigBye() - } - } else { - if c.GetDir() == "in" { - c.ch.deleteConn(c.GetRemoteAddress(), c.GetRemoteLogicalName()) - } - return errors.New("receiveMsg : can not read value of " + msgType) - } + return nil +} + +// to send a message withour having to access the writer externally +func (shosetConn *ShosetConn) SendMessage(msg msg.Message) error { + writer := shosetConn.GetWriter() + if writer == nil { + return errors.New("SendMessage: writer is nil") } - if !ok { - if c.GetDir() == "in" { - c.ch.deleteConn(c.GetRemoteAddress(), c.GetRemoteLogicalName()) + return writer.SendMessage(msg) +} + +// to get TCPInfo : info about the connection (RTT, congestion window, lost packets,...) +func (shosetConn *ShosetConn) GetTCPInfo() (*syscall.TCPInfo, error) { + shosetConn.mu.Lock() + defer shosetConn.mu.Unlock() + return shosetConn.getTCPInfo(shosetConn.conn) +} + +// to get TCPInfo using syscall on linux +func (shosetConn *ShosetConn) getTCPInfo(conn net.Conn) (*syscall.TCPInfo, error) { + type tc struct { + underConn net.Conn + } + var rawConn syscall.RawConn + var err error + tconn := (*tc)(unsafe.Pointer(conn.(*tls.Conn))) + if tcpConn, ok := tconn.underConn.(*net.TCPConn); ok { + rawConn, err = tcpConn.SyscallConn() + if err != nil { + return nil, err } - return errors.New("receiveMsg : non implemented type of message " + msgType) + } else { + return nil, errors.New("tls under conn is not *net.TCPConn") } - time.Sleep(time.Millisecond * time.Duration(100)) // maybe we can remove this sleep time - return nil + + tcpInfo := syscall.TCPInfo{} + size := unsafe.Sizeof(tcpInfo) + var errno syscall.Errno + err = rawConn.Control(func(fd uintptr) { + _, _, errno = syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.SOL_TCP, syscall.TCP_INFO, + uintptr(unsafe.Pointer(&tcpInfo)), uintptr(unsafe.Pointer(&size)), 0) + }) + if err != nil { + return nil, fmt.Errorf("rawconn control failed. err=%v", err) + } + + if errno != 0 { + return nil, fmt.Errorf("syscall failed. errno=%d", errno) + } + return &tcpInfo, nil +} + +func (shosetConn *ShosetConn) GetLogger() *zerolog.Logger { + return &shosetConn.Logger } diff --git a/test2.dot b/svg/test2.dot similarity index 100% rename from test2.dot rename to svg/test2.dot diff --git a/test2.svg b/svg/test2.svg similarity index 100% rename from test2.svg rename to svg/test2.svg diff --git a/test2_post.dot b/svg/test2_post.dot similarity index 100% rename from test2_post.dot rename to svg/test2_post.dot diff --git a/test2_post.svg b/svg/test2_post.svg similarity index 100% rename from test2_post.svg rename to svg/test2_post.svg diff --git a/syncFiles.go b/syncFiles.go new file mode 100644 index 0000000..99b8561 --- /dev/null +++ b/syncFiles.go @@ -0,0 +1,38 @@ +package shoset + +import ( + fileMod "github.com/ditrit/shoset/file" + msg "github.com/ditrit/shoset/msg" +) + +type SyncFiles struct { + fileMod.FileLibrary +} + +func (s *Shoset) InitLibrary(baseDirectory string) { + s.mu.Lock() + defer s.mu.Unlock() + s.baseDirectory = baseDirectory + var err error + s.Library, err = fileMod.NewFileLibrary(baseDirectory) + if err != nil { + s.Logger.Error().Msg(err.Error()) + } + s.Handlers["file"].(*FileHandler).Init(s.Library, s.Logger, s.Queue["file"], func(message *msg.FileMessage) { s.Handlers["file"].(*FileHandler).Send(s, message) }) +} + +func (s *Shoset) GetBaseDirectory() string { + s.mu.RLock() + defer s.mu.RUnlock() + return s.baseDirectory +} + +func (s *Shoset) SyncLibrary(conn *ShosetConn) { + publishMessage, err := s.Library.GetMessageLibrary() + if err != nil { + s.Logger.Error().Msg(err.Error()) + return + } + conn.SendMessage(publishMessage) + conn.GetShoset().Logger.Trace().Msg("-------------SyncLibrary " + conn.GetLocalAddress() + " with " + conn.GetRemoteAddress()) +} diff --git a/test/example/example.go b/test/example/example.go new file mode 100644 index 0000000..eec7e46 --- /dev/null +++ b/test/example/example.go @@ -0,0 +1,359 @@ +package example + +import ( + "fmt" + "time" + + "github.com/ditrit/shoset" + "github.com/ditrit/shoset/msg" + utilsForTest "github.com/ditrit/shoset/test/utils_for_test" +) + +// Simplest example : +func SimpleExample() { + cl1 := shoset.NewShoset("A", "TypeOfA") + cl1.InitPKI("localhost:8001") // Is the CA of the network + + cl2 := shoset.NewShoset("B", "TypeOfB") + cl2.Protocol("localhost:8002", "localhost:8001", "link") // we link it to our first socket + + cl1.WaitForProtocols(5) // Wait for cl1 to be ready + cl2.WaitForProtocols(5) + + //Sender : + event := msg.NewEventClassic("test_topic", "test_event", "test_payload") + cl2.Send(event) + + //Receiver : + event_rc := cl1.Wait("evt", map[string]string{"topic": "test_topic", "event": "test_event"}, 5, nil) + fmt.Println("event received : ", event_rc) + if event_rc != nil { + shoset.Log("event received (Payload) : " + event_rc.GetPayload()) + } +} + +// Send an event every second forever : +func TestEventContinuousSend() { + cl1 := shoset.NewShoset("A", "cl") + cl1.InitPKI("localhost:8001") // Is the CA of the network + + cl2 := shoset.NewShoset("B", "cl") + cl2.Protocol("localhost:8002", "localhost:8001", "link") // we link it to our first socket + + cl1.WaitForProtocols(5) // Wait for cl1 to be ready + cl2.WaitForProtocols(5) + + //Sender : + go func() { + i := 0 + for { + time.Sleep(1 * time.Second) + event := msg.NewEventClassic("test_topic", "test_event", "test_payload"+fmt.Sprint(i)) + cl2.Send(event) + //fmt.Println("event (sender) : ", event) + i++ + } + }() + + //Receiver : + iterator := msg.NewIterator(cl1.Queue["evt"]) + go func() { + for { + event_rc := cl1.Wait("evt", map[string]string{"topic": "test_topic", "event": "test_event"}, 5, iterator) + fmt.Println("event received : ", event_rc) + if event_rc != nil { + shoset.Log("event received (Payload) : " + event_rc.GetPayload()) + } + } + }() + + // Do someting else while it is sending and receiving messages. + + select {} //Never return +} + +// Forwarding : Send a message every second from C to A forever. +func TestSimpleForwarding() { + cl1 := shoset.NewShoset("A", "cl") + cl1.InitPKI("localhost:8001") // Is the CA of the network + + cl2 := shoset.NewShoset("B", "cl") + cl2.Protocol("localhost:8002", "localhost:8001", "link") // we link it to our first socket + + cl3 := shoset.NewShoset("C", "cl") + cl3.Protocol("localhost:8003", "localhost:8002", "link") + + cl1.WaitForProtocols(5) // Wait for cl1 to be ready + cl2.WaitForProtocols(5) + cl3.WaitForProtocols(5) + + //Sender : + go func() { + i := 0 + for { + time.Sleep(1 * time.Second) + event := msg.NewSimpleMessage("A", "test_payload"+fmt.Sprint(i)) + cl3.Send(event) + i++ + } + }() + + //Receiver : + iterator := msg.NewIterator(cl1.Queue["simpleMessage"]) + go func() { + for { + event_rc := cl1.Wait("simpleMessage", map[string]string{}, 5, iterator) + fmt.Println("message received : ", event_rc) + if event_rc != nil { + shoset.Log("message received (Payload) : " + event_rc.GetPayload()) + } + } + }() + + // Do someting else while it is sending and receiving messages. + + select {} //Never return +} + +// Forwarding using topology : Same as before but using the topology sytem to symplify setup. +func TestForwardingTopology() { + tt := utilsForTest.Circle // Choose the network topology for the test + + s := []*shoset.Shoset{} // Create the empty list of shosets + + s = utilsForTest.CreateManyShosets(tt, s, false) // Populate the list with the shosets as specified in the selected topology and estavlish connection among them + + utilsForTest.WaitForManyShosets(s) // Wait for every shosets in the list to be ready + + utilsForTest.PrintManyShosets(s) // Display the info of every shosets in the list + + destination := s[len(s)-1] + sender := s[0] + + //Sender : + go func() { + i := 0 + for { + time.Sleep(1 * time.Second) + message := msg.NewSimpleMessage(destination.GetLogicalName(), "test_payload"+fmt.Sprint(i)) + sender.Send(message) + i++ + if i >= 10 { + break + } + } + }() + + //Receiver : + iterator := msg.NewIterator(destination.Queue["simpleMessage"]) + go func() { + for { + event_rc := destination.Wait("simpleMessage", map[string]string{}, 5, iterator) + fmt.Println("message received : ", event_rc) + if event_rc != nil { + shoset.Log("message received (Payload) : " + event_rc.GetPayload()) + } + } + }() + + // Do someting else while it is sending and receiving messages. + + time.Sleep(5 * time.Second) // Wait for the routing to be established + utilsForTest.PrintManyShosets(s) + time.Sleep(10 * time.Second) // Wait for the end of the test + + //select {} //Never return +} + +func TestMutltipleShosets() { + fmt.Println("TestMultipleShosets") + tt := utilsForTest.Line2With3Shosets // Choose the network topology for the test + + s := []*shoset.Shoset{} // Create the empty list of shosets + + s = utilsForTest.CreateManyShosets(tt, s, false) // Populate the list with the shosets as specified in the selected topology and estavlish connection among them + + utilsForTest.WaitForManyShosets(s) // Wait for every shosets in the list to be ready + + time.Sleep(3 * time.Second) + + utilsForTest.PrintManyShosets(s) // Display the info of every shosets in the list + + time.Sleep(20 * time.Second) + + //select {} //Never return +} + +func Test3Shosets() { + + tt := utilsForTest.Line2With2Shosets // Choose the network topology for the test + + s := []*shoset.Shoset{} // Create the empty list of shosets + + s = utilsForTest.CreateManyShosets(tt, s, false) // Populate the list with the shosets as specified in the selected topology and estavlish connection among them + + utilsForTest.WaitForManyShosets(s) // Wait for every shosets in the list to be ready + + time.Sleep(3 * time.Second) + + utilsForTest.PrintManyShosets(s) // Display the info of every shosets in the list + + //Sender : + go func() { + i := 0 + for { + time.Sleep(1 * time.Second) + event := msg.NewEventClassic("test_topic", "test_event", "test_payload"+fmt.Sprint(i)) + s[0].Send(event) + fmt.Println("A send event to B1 : ", "test_payload"+fmt.Sprint(i)) + i++ + } + }() + + //Receiver : + iterator := msg.NewIterator(s[1].Queue["evt"]) + go func() { + for { + event_rc := s[1].Wait("evt", map[string]string{"topic": "test_topic", "event": "test_event"}, 5, iterator) + if event_rc != nil { + fmt.Println("B1 message received (Payload) : " + event_rc.GetPayload()) + } + } + }() + + //Receiver : + iterator2 := msg.NewIterator(s[2].Queue["evt"]) + go func() { + for { + event_rc := s[2].Wait("evt", map[string]string{"topic": "test_topic", "event": "test_event"}, 5, iterator2) + if event_rc != nil { + fmt.Println("B2 message received (Payload) : " + event_rc.GetPayload()) + } + } + }() + + time.Sleep(20 * time.Second) +} + +// test with A-B-B +func Test3ShosetsCommand() { + fmt.Println("Test3ShosetsCommand : \n A-B1-B2 \n A send 'command' to B1 \n Normally the command is transmitted to B2") + tt := utilsForTest.Line2With2Shosets // Choose the network topology for the test + + s := []*shoset.Shoset{} // Create the empty list of shosets + + s = utilsForTest.CreateManyShosets(tt, s, false) // Populate the list with the shosets as specified in the selected topology and estavlish connection among them + + utilsForTest.WaitForManyShosets(s) // Wait for every shosets in the list to be ready + + //utilsForTest.PrintManyShosets(s) // Display the info of every shosets in the list + + //destination := s[1] // B far from B + sender := s[0] // A pki + + //Sender : + go func() { + i := 0 + for { + time.Sleep(1 * time.Second) + message := msg.NewCommand("B", "test_command", "test_payload"+fmt.Sprint(i)) + fmt.Println("A send command to B1 : ", "test_payload"+fmt.Sprint(i)) + sender.Send(*message) + i++ + if i >= 10 { + break + } + } + }() + + //Receiver : + iterator := msg.NewIterator(s[1].Queue["cmd"]) + go func() { + for { + event_rc := s[1].Wait("cmd", map[string]string{"name": "test_command"}, 5, iterator) + if event_rc != nil { + shoset.Log("B1 message received (Payload) : " + event_rc.GetPayload()) + } + } + }() + iterator2 := msg.NewIterator(s[2].Queue["cmd"]) + rcvd := 0 + go func() { + for { + event_rc2 := s[2].Wait("cmd", map[string]string{"name": "test_command"}, 5, iterator2) + if event_rc2 != nil { + shoset.Log("B2 message received (Payload) : " + event_rc2.GetPayload()) + rcvd += 1 + } + } + }() + + // Do someting else while it is sending and receiving messages. + + time.Sleep(3 * time.Second) // Wait for the routing to be established + + time.Sleep(5 * time.Second) // Wait for the end of the test + fmt.Println(rcvd) + utilsForTest.PrintManyShosets(s) // Display the info of every shosets in the list + + //select {} //Never return +} + +// test with A-B-B +func Test2ShosetsCommand() { + fmt.Println("Test2ShosetsCommand : \n A-B \n A send 'command' to B") + tt := utilsForTest.Line2 // Choose the network topology for the test + + s := []*shoset.Shoset{} // Create the empty list of shosets + + s = utilsForTest.CreateManyShosets(tt, s, false) // Populate the list with the shosets as specified in the selected topology and estavlish connection among them + + utilsForTest.WaitForManyShosets(s) // Wait for every shosets in the list to be ready + + //utilsForTest.PrintManyShosets(s) // Display the info of every shosets in the list + + destination := s[1] // B + sender := s[0] // A pki + + //Sender : + go func() { + i := 0 + for { + time.Sleep(1 * time.Second) + message := msg.NewCommand("B", "test_command", "test_payload"+fmt.Sprint(i)) + sender.Send(*message) + i++ + if i >= 10 { + break + } + } + }() + + //Receiver : + iterator := msg.NewIterator(s[1].Queue["cmd"]) + go func() { + for { + event_rc := destination.Wait("cmd", map[string]string{"name": "test_command"}, 5, iterator) + if event_rc != nil { + shoset.Log("B2 message received (Payload) : " + event_rc.GetPayload()) + } + } + }() + iterator2 := msg.NewIterator(s[0].Queue["cmd"]) + go func() { + for { + event_rc := destination.Wait("cmd", map[string]string{"name": "test_command"}, 5, iterator2) + if event_rc != nil { + shoset.Log("B1 message received (Payload) : " + event_rc.GetPayload()) + } + } + }() + + // Do someting else while it is sending and receiving messages. + + time.Sleep(3 * time.Second) // Wait for the routing to be established + utilsForTest.PrintManyShosets(s) + time.Sleep(20 * time.Second) // Wait for the end of the test + + //select {} //Never return +} diff --git a/test/file_test/externalCommands_test.go b/test/file_test/externalCommands_test.go new file mode 100644 index 0000000..5d2bd6c --- /dev/null +++ b/test/file_test/externalCommands_test.go @@ -0,0 +1,25 @@ +package file_test + +import ( + "fmt" + "testing" + + mock "github.com/ditrit/shoset/test/mocks/file" + "github.com/golang/mock/gomock" + + fileMod "github.com/ditrit/shoset/file" +) + +func TestNewCommands(t *testing.T) { + workingDir := cleanDir(t) + createCopyDir(t) + fmt.Println("workingDir", workingDir) + + ctrl := gomock.NewController(t) + fileTransfer := mock.NewMockFileTransfer(ctrl) + ec := fileMod.NewExternalCommands(fileTransfer) + + if ec.FileTransfer != fileTransfer { + t.Errorf("NewExternalCommands() = %v, want %v", ec.FileTransfer, fileTransfer) + } +} diff --git a/test/file_test/fileLibrary_test.go b/test/file_test/fileLibrary_test.go new file mode 100644 index 0000000..e5aa20e --- /dev/null +++ b/test/file_test/fileLibrary_test.go @@ -0,0 +1,98 @@ +package file_test + +import ( + "os" + "path/filepath" + "testing" + + fileMod "github.com/ditrit/shoset/file" +) + +// we assume that file and syncFile tests are ok + +func TestNewFileLibrary(t *testing.T) { + workingDir := cleanDir(t) + fileMod.NewFileLibrary(workingDir) +} + +func TestLoadLibrary(t *testing.T) { + workingDir := cleanDir(t) + createCopyDir(t) + + // we create a syncFile and store the info + syncFile := fileMod.NewSyncFile(workingDir) + CopyFile, err := fileMod.LoadFile(workingDir, filepath.Join(fileMod.PATH_COPY_FILES, "test"), "fileName") + if err != nil { + t.Error(err) + } + err = CopyFile.WriteChunk([]byte("hello"), 0) + if err != nil { + t.Error(err) + } + syncFile.CopyFile = CopyFile + realFile, err := fileMod.LoadFile(workingDir, "test", "fileName") + if err != nil { + t.Error(err) + } + syncFile.RealFile = realFile + + // save the info first + err = syncFile.SaveFileInfo() + if err != nil { + t.Error(err) + } + uuid := syncFile.GetUUID() + + fileLibrary, err := fileMod.NewFileLibrary(workingDir) // it will automatically load the library + if err != nil { + t.Error(err) + } + _, err = fileLibrary.GetFile(uuid) + if err != nil { + t.Error(err) + } + +} + +func TestUploadFile(t *testing.T) { + workingDir := cleanDir(t) + createCopyDir(t) + + // create the library + fileLibrary, err := fileMod.NewFileLibrary(workingDir) // it will automatically load the library + if err != nil { + t.Error(err) + } + + // we create a file + _, err = os.Create(filepath.Join(workingDir, "fileName")) + if err != nil { + t.Error(err) + } + file, err := fileMod.LoadFile(workingDir, "", "fileName") + if err != nil { + t.Error(err) + } + + syncFile, err := fileLibrary.UploadFile(file) + if err != nil { + t.Error(err) + } + if syncFile.GetRealFile() != file { + t.Error("the real file is not the same") + } + + // we check that the file is in the library + _, err = fileLibrary.GetFile(syncFile.GetUUID()) + if err != nil { + t.Error(err) + } + + // the Copy file should have been created + _, err = os.Stat(filepath.Join(workingDir, fileMod.PATH_COPY_FILES, "fileName")) + if err != nil { + t.Error(err) + } +} + +func TestUpdateLibrary(t *testing.T) {} diff --git a/test/file_test/file_test.go b/test/file_test/file_test.go new file mode 100644 index 0000000..39b8788 --- /dev/null +++ b/test/file_test/file_test.go @@ -0,0 +1,89 @@ +package file_test + +import ( + "os" + "path/filepath" + "testing" + + fileMod "github.com/ditrit/shoset/file" +) + +func TestNewEmptyFile(t *testing.T) { + workingDir := cleanDir(t) + file, err := fileMod.NewEmptyFile(workingDir, "relativePath", "fileName", 10, "hash", 1, map[int]string{0: "hash"}) + if err != nil { + t.Errorf("error in constructor : " + err.Error()) + } + if file.GetRelativePath() != "relativePath" { + t.Errorf("file relative path is not relativePath") + } + if file.GetName() != "fileName" { + t.Errorf("file name is not fileName") + } + if file.GetSize() != 10 { + t.Errorf("file size is not 10") + } + if file.GetHash() != "hash" { + t.Errorf("file hash is not hash") + } + if file.GetVersion() != 1 { + t.Errorf("file version is not 1") + } + if file.GetHashMap()[0] != "hash" { + t.Errorf("file hashes is not hash") + } + + fileInfo, err := os.Stat(filepath.Join(workingDir, "relativePath", "fileName")) + if err != nil { + t.Errorf("file not created") + } + if fileInfo.Size() != 10 { + t.Errorf("file size is not 10") + } +} + +func TestLoadFile(t *testing.T) { + workingDir := cleanDir(t) + _, err := fileMod.LoadFile(workingDir, "relativePath", "fileName") + // it normally create a file + if err != nil { + t.Errorf("error in constructor : " + err.Error()) + } + _, err = os.Stat(filepath.Join(workingDir, "relativePath", "fileName")) + if err != nil { + t.Errorf("file not created : ") + } +} + +func TestLoadFileInfo(t *testing.T) { + workingDir := cleanDir(t) + err := os.MkdirAll(filepath.Join(workingDir, "relativePath"), 0755) + if err != nil { + t.Errorf("error in creation : " + err.Error()) + } + fc, err := os.Create(filepath.Join(workingDir, "relativePath", "fileName")) + if err != nil { + t.Errorf("error in creation : " + err.Error()) + } + fc.Close() + + fileInfo := make(map[string]interface{}) + fileInfo["baseFilesDir"] = workingDir + fileInfo["relativePath"] = "relativePath" + fileInfo["name"] = "fileName" + fileInfo["size"] = float64(0) + fileInfo["pieceSize"] = float64(256 * 1024) + fileInfo["nbPieces"] = float64(0) + fileInfo["hash"] = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + fileInfo["version"] = float64(1) + fileInfo["hashMap"] = map[string]interface{}{} + _, err = fileMod.LoadFileInfo(fileInfo) + // it normally create a file + if err != nil { + t.Errorf("error in constructor : " + err.Error()) + } + _, err = os.Stat(filepath.Join(workingDir, "relativePath", "fileName")) + if err != nil { + t.Errorf("file not created") + } +} diff --git a/test/file_test/syncFile_test.go b/test/file_test/syncFile_test.go new file mode 100644 index 0000000..3975f5f --- /dev/null +++ b/test/file_test/syncFile_test.go @@ -0,0 +1,131 @@ +package file_test + +// we assume that the tests from file_test.go are ok + +import ( + "path/filepath" + "testing" + + fileMod "github.com/ditrit/shoset/file" +) + +func TestNewSyncFile(t *testing.T) { + workingDir := cleanDir(t) + fileMod.NewSyncFile(workingDir) +} + +func TestWriting(t *testing.T) { + workingDir := cleanDir(t) + syncFile := fileMod.NewSyncFile(workingDir) + CopyFile, err := fileMod.LoadFile(workingDir, filepath.Join(fileMod.PATH_COPY_FILES, "test"), "fileName") + if err != nil { + t.Error(err) + } + err = CopyFile.WriteChunk([]byte("hello"), 0) + if err != nil { + t.Error(err) + } + syncFile.CopyFile = CopyFile + realFile, err := fileMod.LoadFile(workingDir, "test", "fileName") + if err != nil { + t.Error(err) + } + syncFile.RealFile = realFile + + // test writing Copy to real + err = syncFile.WriteCopyToReal() + if err != nil { + t.Error(err) + } + hello, err := realFile.LoadData(0, 5) + if err != nil { + t.Error(err) + } + if string(hello) != "hello" { + t.Error("file content is not correct : ", string(hello), " instead of hello") + } + + // test writing real to Copy + realFile.WriteChunk([]byte(" world"), 5) + err = syncFile.WriteRealToCopy() + if err != nil { + t.Error(err) + } + + helloWorld, err := CopyFile.LoadData(0, 11) + if err != nil { + t.Error(err) + } + if string(helloWorld) != "hello world" { + t.Error("file content is not correct : ", string(helloWorld), " instead of hello world") + } +} + +func TestSaveFileInfo(t *testing.T) { + workingDir := cleanDir(t) + createCopyDir(t) + syncFile := fileMod.NewSyncFile(workingDir) + CopyFile, err := fileMod.LoadFile(workingDir, filepath.Join(fileMod.PATH_COPY_FILES, "test"), "fileName") + if err != nil { + t.Error(err) + } + err = CopyFile.WriteChunk([]byte("hello"), 0) + if err != nil { + t.Error(err) + } + syncFile.CopyFile = CopyFile + realFile, err := fileMod.LoadFile(workingDir, "test", "fileName") + if err != nil { + t.Error(err) + } + syncFile.RealFile = realFile + + err = syncFile.SaveFileInfo() + if err != nil { + t.Error(err) + } +} + +func TestLoadSyncFileInfo(t *testing.T) { + workingDir := cleanDir(t) + createCopyDir(t) + syncFile := fileMod.NewSyncFile(workingDir) + CopyFile, err := fileMod.LoadFile(workingDir, filepath.Join(fileMod.PATH_COPY_FILES, "test"), "fileName") + if err != nil { + t.Error(err) + } + err = CopyFile.WriteChunk([]byte("hello"), 0) + if err != nil { + t.Error(err) + } + syncFile.CopyFile = CopyFile + realFile, err := fileMod.LoadFile(workingDir, "test", "fileName") + if err != nil { + t.Error(err) + } + syncFile.RealFile = realFile + + // save the info first + err = syncFile.SaveFileInfo() + if err != nil { + t.Error(err) + } + uuid := syncFile.GetUUID() + + syncFile2, err := fileMod.LoadSyncFileInfo(workingDir, filepath.Join(workingDir, fileMod.PATH_COPY_FILES, ".info", uuid+".info")) + if err != nil { + t.Error(err) + } + if syncFile2.CopyFile.GetName() != "fileName" { + t.Error("file name is not correct") + } + if syncFile2.CopyFile.GetRelativePath() != filepath.Join(fileMod.PATH_COPY_FILES, "test") { + t.Error("file relative path is not correct") + } + if syncFile2.RealFile.GetName() != "fileName" { + t.Error("file name is not correct") + } + if syncFile2.RealFile.GetRelativePath() != "test" { + t.Error("file relative path is not correct") + } +} diff --git a/test/file_test/utils_for_test.go b/test/file_test/utils_for_test.go new file mode 100644 index 0000000..b4a007a --- /dev/null +++ b/test/file_test/utils_for_test.go @@ -0,0 +1,33 @@ +package file_test + +import ( + "os" + "path/filepath" + "testing" +) + +func getBaseDir(t *testing.T) string { + workingDir, err := os.Getwd() + if err != nil { + t.Errorf(err.Error()) + } + if filepath.Base(workingDir) == "file_test" { + workingDir = filepath.Dir(workingDir) + } + baseDir := filepath.Join(workingDir, "testRep") + return baseDir +} + +func cleanDir(t *testing.T) string { + baseDir := getBaseDir(t) + + os.RemoveAll(baseDir) + os.MkdirAll(baseDir, 0755) + return baseDir +} + +func createCopyDir(t *testing.T) { + workingDir := getBaseDir(t) + CopyDir := filepath.Join(workingDir, ".copy", ".info") + os.MkdirAll(CopyDir, 0755) +} diff --git a/test/file_test/utils_test.go b/test/file_test/utils_test.go new file mode 100644 index 0000000..c924f05 --- /dev/null +++ b/test/file_test/utils_test.go @@ -0,0 +1,25 @@ +package file_test + +import ( + "testing" + + fileMod "github.com/ditrit/shoset/file" +) + +func TestRealToCopyPath(t *testing.T) { + path := "hello/Downloads/1.txt" + expected := ".copy/hello/Downloads/1.txt" + result := fileMod.RealToCopyPath(path) + if result != expected { + t.Errorf("Expected %s, got %s", expected, result) + } +} + +func TestCopyToRealPath(t *testing.T) { + path := ".copy/hello/Downloads/1.txt" + expected := "hello/Downloads/1.txt" + result := fileMod.CopyToRealPath(path) + if result != expected { + t.Errorf("Expected %s, got %s", expected, result) + } +} diff --git a/test/generateMocks.sh b/test/generateMocks.sh new file mode 100755 index 0000000..5cfb75e --- /dev/null +++ b/test/generateMocks.sh @@ -0,0 +1,3 @@ +mockgen -destination="test/mocks/file/fileLibrary.go" -source="file/fileLibrary.go" +mockgen -destination="test/mocks/file/file.go" -source="file/file.go" +mockgen -destination="test/mocks/file/fileTransfer.go" -source="file/fileTransfer.go" \ No newline at end of file diff --git a/test/mocks/file/file.go b/test/mocks/file/file.go new file mode 100644 index 0000000..da345b3 --- /dev/null +++ b/test/mocks/file/file.go @@ -0,0 +1,361 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: file/file.go + +// Package mock_fileSync is a generated GoMock package. +package mock_fileSync + +import ( + os "os" + reflect "reflect" + + gomock "github.com/golang/mock/gomock" +) + +// MockFile is a mock of File interface. +type MockFile struct { + ctrl *gomock.Controller + recorder *MockFileMockRecorder +} + +// MockFileMockRecorder is the mock recorder for MockFile. +type MockFileMockRecorder struct { + mock *MockFile +} + +// NewMockFile creates a new mock instance. +func NewMockFile(ctrl *gomock.Controller) *MockFile { + mock := &MockFile{ctrl: ctrl} + mock.recorder = &MockFileMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFile) EXPECT() *MockFileMockRecorder { + return m.recorder +} + +// CalculateHash mocks base method. +func (m *MockFile) CalculateHash() (string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CalculateHash") + ret0, _ := ret[0].(string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CalculateHash indicates an expected call of CalculateHash. +func (mr *MockFileMockRecorder) CalculateHash() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalculateHash", reflect.TypeOf((*MockFile)(nil).CalculateHash)) +} + +// CalculateHashMap mocks base method. +func (m *MockFile) CalculateHashMap() (map[int]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CalculateHashMap") + ret0, _ := ret[0].(map[int]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CalculateHashMap indicates an expected call of CalculateHashMap. +func (mr *MockFileMockRecorder) CalculateHashMap() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalculateHashMap", reflect.TypeOf((*MockFile)(nil).CalculateHashMap)) +} + +// CloseFile mocks base method. +func (m *MockFile) CloseFile() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "CloseFile") +} + +// CloseFile indicates an expected call of CloseFile. +func (mr *MockFileMockRecorder) CloseFile() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseFile", reflect.TypeOf((*MockFile)(nil).CloseFile)) +} + +// GetFileInfoMap mocks base method. +func (m *MockFile) GetFileInfoMap() map[string]interface{} { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFileInfoMap") + ret0, _ := ret[0].(map[string]interface{}) + return ret0 +} + +// GetFileInfoMap indicates an expected call of GetFileInfoMap. +func (mr *MockFileMockRecorder) GetFileInfoMap() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFileInfoMap", reflect.TypeOf((*MockFile)(nil).GetFileInfoMap)) +} + +// GetHash mocks base method. +func (m *MockFile) GetHash() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetHash") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetHash indicates an expected call of GetHash. +func (mr *MockFileMockRecorder) GetHash() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHash", reflect.TypeOf((*MockFile)(nil).GetHash)) +} + +// GetHashChunk mocks base method. +func (m *MockFile) GetHashChunk(index int) string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetHashChunk", index) + ret0, _ := ret[0].(string) + return ret0 +} + +// GetHashChunk indicates an expected call of GetHashChunk. +func (mr *MockFileMockRecorder) GetHashChunk(index interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHashChunk", reflect.TypeOf((*MockFile)(nil).GetHashChunk), index) +} + +// GetHashMap mocks base method. +func (m *MockFile) GetHashMap() map[int]string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetHashMap") + ret0, _ := ret[0].(map[int]string) + return ret0 +} + +// GetHashMap indicates an expected call of GetHashMap. +func (mr *MockFileMockRecorder) GetHashMap() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHashMap", reflect.TypeOf((*MockFile)(nil).GetHashMap)) +} + +// GetName mocks base method. +func (m *MockFile) GetName() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetName") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetName indicates an expected call of GetName. +func (mr *MockFileMockRecorder) GetName() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetName", reflect.TypeOf((*MockFile)(nil).GetName)) +} + +// GetPieceSize mocks base method. +func (m *MockFile) GetPieceSize() int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPieceSize") + ret0, _ := ret[0].(int) + return ret0 +} + +// GetPieceSize indicates an expected call of GetPieceSize. +func (mr *MockFileMockRecorder) GetPieceSize() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPieceSize", reflect.TypeOf((*MockFile)(nil).GetPieceSize)) +} + +// GetRelativePath mocks base method. +func (m *MockFile) GetRelativePath() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRelativePath") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetRelativePath indicates an expected call of GetRelativePath. +func (mr *MockFileMockRecorder) GetRelativePath() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRelativePath", reflect.TypeOf((*MockFile)(nil).GetRelativePath)) +} + +// GetSize mocks base method. +func (m *MockFile) GetSize() int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSize") + ret0, _ := ret[0].(int64) + return ret0 +} + +// GetSize indicates an expected call of GetSize. +func (mr *MockFileMockRecorder) GetSize() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSize", reflect.TypeOf((*MockFile)(nil).GetSize)) +} + +// GetVersion mocks base method. +func (m *MockFile) GetVersion() int { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetVersion") + ret0, _ := ret[0].(int) + return ret0 +} + +// GetVersion indicates an expected call of GetVersion. +func (mr *MockFileMockRecorder) GetVersion() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVersion", reflect.TypeOf((*MockFile)(nil).GetVersion)) +} + +// LoadData mocks base method. +func (m *MockFile) LoadData(chunk int64, chunkSize int) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LoadData", chunk, chunkSize) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// LoadData indicates an expected call of LoadData. +func (mr *MockFileMockRecorder) LoadData(chunk, chunkSize interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadData", reflect.TypeOf((*MockFile)(nil).LoadData), chunk, chunkSize) +} + +// LoadFromMap mocks base method. +func (m *MockFile) LoadFromMap(fileInfoMap map[string]interface{}) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "LoadFromMap", fileInfoMap) +} + +// LoadFromMap indicates an expected call of LoadFromMap. +func (mr *MockFileMockRecorder) LoadFromMap(fileInfoMap interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadFromMap", reflect.TypeOf((*MockFile)(nil).LoadFromMap), fileInfoMap) +} + +// Move mocks base method. +func (m *MockFile) Move(newRelativePath, newName string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Move", newRelativePath, newName) + ret0, _ := ret[0].(error) + return ret0 +} + +// Move indicates an expected call of Move. +func (mr *MockFileMockRecorder) Move(newRelativePath, newName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Move", reflect.TypeOf((*MockFile)(nil).Move), newRelativePath, newName) +} + +// OpenFile mocks base method. +func (m *MockFile) OpenFile() (*os.File, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "OpenFile") + ret0, _ := ret[0].(*os.File) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// OpenFile indicates an expected call of OpenFile. +func (mr *MockFileMockRecorder) OpenFile() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenFile", reflect.TypeOf((*MockFile)(nil).OpenFile)) +} + +// SetHash mocks base method. +func (m *MockFile) SetHash(hash string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetHash", hash) +} + +// SetHash indicates an expected call of SetHash. +func (mr *MockFileMockRecorder) SetHash(hash interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHash", reflect.TypeOf((*MockFile)(nil).SetHash), hash) +} + +// SetHashMap mocks base method. +func (m *MockFile) SetHashMap(hashMap map[int]string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetHashMap", hashMap) +} + +// SetHashMap indicates an expected call of SetHashMap. +func (mr *MockFileMockRecorder) SetHashMap(hashMap interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHashMap", reflect.TypeOf((*MockFile)(nil).SetHashMap), hashMap) +} + +// SetName mocks base method. +func (m *MockFile) SetName(name string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetName", name) +} + +// SetName indicates an expected call of SetName. +func (mr *MockFileMockRecorder) SetName(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetName", reflect.TypeOf((*MockFile)(nil).SetName), name) +} + +// SetRelativePath mocks base method. +func (m *MockFile) SetRelativePath(relativePath string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetRelativePath", relativePath) +} + +// SetRelativePath indicates an expected call of SetRelativePath. +func (mr *MockFileMockRecorder) SetRelativePath(relativePath interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetRelativePath", reflect.TypeOf((*MockFile)(nil).SetRelativePath), relativePath) +} + +// SetVersion mocks base method. +func (m *MockFile) SetVersion(version int) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetVersion", version) +} + +// SetVersion indicates an expected call of SetVersion. +func (mr *MockFileMockRecorder) SetVersion(version interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetVersion", reflect.TypeOf((*MockFile)(nil).SetVersion), version) +} + +// String mocks base method. +func (m *MockFile) String() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "String") + ret0, _ := ret[0].(string) + return ret0 +} + +// String indicates an expected call of String. +func (mr *MockFileMockRecorder) String() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "String", reflect.TypeOf((*MockFile)(nil).String)) +} + +// UpdateMetadata mocks base method. +func (m *MockFile) UpdateMetadata() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdateMetadata") + ret0, _ := ret[0].(error) + return ret0 +} + +// UpdateMetadata indicates an expected call of UpdateMetadata. +func (mr *MockFileMockRecorder) UpdateMetadata() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateMetadata", reflect.TypeOf((*MockFile)(nil).UpdateMetadata)) +} + +// WriteChunk mocks base method. +func (m *MockFile) WriteChunk(chunk []byte, offset int64) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "WriteChunk", chunk, offset) + ret0, _ := ret[0].(error) + return ret0 +} + +// WriteChunk indicates an expected call of WriteChunk. +func (mr *MockFileMockRecorder) WriteChunk(chunk, offset interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteChunk", reflect.TypeOf((*MockFile)(nil).WriteChunk), chunk, offset) +} diff --git a/test/mocks/file/fileLibrary.go b/test/mocks/file/fileLibrary.go new file mode 100644 index 0000000..e3963e9 --- /dev/null +++ b/test/mocks/file/fileLibrary.go @@ -0,0 +1,242 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: file/fileLibrary.go + +// Package mock_fileSync is a generated GoMock package. +package mock_fileSync + +import ( + reflect "reflect" + + fileSync "github.com/ditrit/shoset/file" + msg "github.com/ditrit/shoset/msg" + gomock "github.com/golang/mock/gomock" +) + +// MockFileLibrary is a mock of FileLibrary interface. +type MockFileLibrary struct { + ctrl *gomock.Controller + recorder *MockFileLibraryMockRecorder +} + +// MockFileLibraryMockRecorder is the mock recorder for MockFileLibrary. +type MockFileLibraryMockRecorder struct { + mock *MockFileLibrary +} + +// NewMockFileLibrary creates a new mock instance. +func NewMockFileLibrary(ctrl *gomock.Controller) *MockFileLibrary { + mock := &MockFileLibrary{ctrl: ctrl} + mock.recorder = &MockFileLibraryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFileLibrary) EXPECT() *MockFileLibraryMockRecorder { + return m.recorder +} + +// Add mocks base method. +func (m *MockFileLibrary) Add(libraryPath string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Add", libraryPath) + ret0, _ := ret[0].(error) + return ret0 +} + +// Add indicates an expected call of Add. +func (mr *MockFileLibraryMockRecorder) Add(libraryPath interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockFileLibrary)(nil).Add), libraryPath) +} + +// CreateFile mocks base method. +func (m *MockFileLibrary) CreateFile(file fileSync.File, uuid string) (fileSync.SyncFile, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateFile", file, uuid) + ret0, _ := ret[0].(fileSync.SyncFile) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateFile indicates an expected call of CreateFile. +func (mr *MockFileLibraryMockRecorder) CreateFile(file, uuid interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateFile", reflect.TypeOf((*MockFileLibrary)(nil).CreateFile), file, uuid) +} + +// DeleteFile mocks base method. +func (m *MockFileLibrary) DeleteFile(uuid string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DeleteFile", uuid) +} + +// DeleteFile indicates an expected call of DeleteFile. +func (mr *MockFileLibraryMockRecorder) DeleteFile(uuid interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteFile", reflect.TypeOf((*MockFileLibrary)(nil).DeleteFile), uuid) +} + +// GetDir mocks base method. +func (m *MockFileLibrary) GetDir() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetDir") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetDir indicates an expected call of GetDir. +func (mr *MockFileLibraryMockRecorder) GetDir() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDir", reflect.TypeOf((*MockFileLibrary)(nil).GetDir)) +} + +// GetFile mocks base method. +func (m *MockFileLibrary) GetFile(uuid string) (fileSync.SyncFile, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFile", uuid) + ret0, _ := ret[0].(fileSync.SyncFile) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetFile indicates an expected call of GetFile. +func (mr *MockFileLibraryMockRecorder) GetFile(uuid interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFile", reflect.TypeOf((*MockFileLibrary)(nil).GetFile), uuid) +} + +// GetHash mocks base method. +func (m *MockFileLibrary) GetHash() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetHash") + ret0, _ := ret[0].(string) + return ret0 +} + +// GetHash indicates an expected call of GetHash. +func (mr *MockFileLibraryMockRecorder) GetHash() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHash", reflect.TypeOf((*MockFileLibrary)(nil).GetHash)) +} + +// GetMessageLibrary mocks base method. +func (m *MockFileLibrary) GetMessageLibrary() (*msg.FileMessage, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetMessageLibrary") + ret0, _ := ret[0].(*msg.FileMessage) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetMessageLibrary indicates an expected call of GetMessageLibrary. +func (mr *MockFileLibraryMockRecorder) GetMessageLibrary() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMessageLibrary", reflect.TypeOf((*MockFileLibrary)(nil).GetMessageLibrary)) +} + +// LoadLibrary mocks base method. +func (m *MockFileLibrary) LoadLibrary() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "LoadLibrary") + ret0, _ := ret[0].(error) + return ret0 +} + +// LoadLibrary indicates an expected call of LoadLibrary. +func (mr *MockFileLibraryMockRecorder) LoadLibrary() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadLibrary", reflect.TypeOf((*MockFileLibrary)(nil).LoadLibrary)) +} + +// Modify mocks base method. +func (m *MockFileLibrary) Modify(libraryPath string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Modify", libraryPath) + ret0, _ := ret[0].(error) + return ret0 +} + +// Modify indicates an expected call of Modify. +func (mr *MockFileLibraryMockRecorder) Modify(libraryPath interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Modify", reflect.TypeOf((*MockFileLibrary)(nil).Modify), libraryPath) +} + +// Move mocks base method. +func (m *MockFileLibrary) Move(from, to string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Move", from, to) + ret0, _ := ret[0].(error) + return ret0 +} + +// Move indicates an expected call of Move. +func (mr *MockFileLibraryMockRecorder) Move(from, to interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Move", reflect.TypeOf((*MockFileLibrary)(nil).Move), from, to) +} + +// PrintAllFiles mocks base method. +func (m *MockFileLibrary) PrintAllFiles() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "PrintAllFiles") +} + +// PrintAllFiles indicates an expected call of PrintAllFiles. +func (mr *MockFileLibraryMockRecorder) PrintAllFiles() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrintAllFiles", reflect.TypeOf((*MockFileLibrary)(nil).PrintAllFiles)) +} + +// Remove mocks base method. +func (m *MockFileLibrary) Remove(path string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Remove", path) + ret0, _ := ret[0].(error) + return ret0 +} + +// Remove indicates an expected call of Remove. +func (mr *MockFileLibraryMockRecorder) Remove(path interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockFileLibrary)(nil).Remove), path) +} + +// SetFileTransfer mocks base method. +func (m *MockFileLibrary) SetFileTransfer(fileTransfer fileSync.FileTransfer) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetFileTransfer", fileTransfer) +} + +// SetFileTransfer indicates an expected call of SetFileTransfer. +func (mr *MockFileLibraryMockRecorder) SetFileTransfer(fileTransfer interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetFileTransfer", reflect.TypeOf((*MockFileLibrary)(nil).SetFileTransfer), fileTransfer) +} + +// UpdateLibrary mocks base method. +func (m *MockFileLibrary) UpdateLibrary(listFiles []fileSync.FileState, conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UpdateLibrary", listFiles, conn) +} + +// UpdateLibrary indicates an expected call of UpdateLibrary. +func (mr *MockFileLibraryMockRecorder) UpdateLibrary(listFiles, conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateLibrary", reflect.TypeOf((*MockFileLibrary)(nil).UpdateLibrary), listFiles, conn) +} + +// UploadFile mocks base method. +func (m *MockFileLibrary) UploadFile(file fileSync.File) (fileSync.SyncFile, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UploadFile", file) + ret0, _ := ret[0].(fileSync.SyncFile) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UploadFile indicates an expected call of UploadFile. +func (mr *MockFileLibraryMockRecorder) UploadFile(file interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UploadFile", reflect.TypeOf((*MockFileLibrary)(nil).UploadFile), file) +} diff --git a/test/mocks/file/fileTransfer.go b/test/mocks/file/fileTransfer.go new file mode 100644 index 0000000..9621178 --- /dev/null +++ b/test/mocks/file/fileTransfer.go @@ -0,0 +1,561 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: file/fileTransfer.go + +// Package mock_fileSync is a generated GoMock package. +package mock_fileSync + +import ( + reflect "reflect" + + fileSync "github.com/ditrit/shoset/file" + msg "github.com/ditrit/shoset/msg" + gomock "github.com/golang/mock/gomock" + zerolog "github.com/rs/zerolog" +) + +// MockFileTransfer is a mock of FileTransfer interface. +type MockFileTransfer struct { + ctrl *gomock.Controller + recorder *MockFileTransferMockRecorder +} + +// MockFileTransferMockRecorder is the mock recorder for MockFileTransfer. +type MockFileTransferMockRecorder struct { + mock *MockFileTransfer +} + +// NewMockFileTransfer creates a new mock instance. +func NewMockFileTransfer(ctrl *gomock.Controller) *MockFileTransfer { + mock := &MockFileTransfer{ctrl: ctrl} + mock.recorder = &MockFileTransferMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFileTransfer) EXPECT() *MockFileTransferMockRecorder { + return m.recorder +} + +// AddFileLeecher mocks base method. +func (m *MockFileTransfer) AddFileLeecher(fileLeecher *fileSync.FileLeecher) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddFileLeecher", fileLeecher) +} + +// AddFileLeecher indicates an expected call of AddFileLeecher. +func (mr *MockFileTransferMockRecorder) AddFileLeecher(fileLeecher interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddFileLeecher", reflect.TypeOf((*MockFileTransfer)(nil).AddFileLeecher), fileLeecher) +} + +// AddFileLeecherToConn mocks base method. +func (m *MockFileTransfer) AddFileLeecherToConn(fileLeecher *fileSync.FileLeecher, conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddFileLeecherToConn", fileLeecher, conn) +} + +// AddFileLeecherToConn indicates an expected call of AddFileLeecherToConn. +func (mr *MockFileTransferMockRecorder) AddFileLeecherToConn(fileLeecher, conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddFileLeecherToConn", reflect.TypeOf((*MockFileTransfer)(nil).AddFileLeecherToConn), fileLeecher, conn) +} + +// AddUploadConn mocks base method. +func (m *MockFileTransfer) AddUploadConn(conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AddUploadConn", conn) +} + +// AddUploadConn indicates an expected call of AddUploadConn. +func (mr *MockFileTransferMockRecorder) AddUploadConn(conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUploadConn", reflect.TypeOf((*MockFileTransfer)(nil).AddUploadConn), conn) +} + +// AskChunk mocks base method. +func (m *MockFileTransfer) AskChunk(conn fileSync.ShosetConn, message *msg.FileMessage, sendRate bool) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AskChunk", conn, message, sendRate) +} + +// AskChunk indicates an expected call of AskChunk. +func (mr *MockFileTransferMockRecorder) AskChunk(conn, message, sendRate interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskChunk", reflect.TypeOf((*MockFileTransfer)(nil).AskChunk), conn, message, sendRate) +} + +// AskInfoFile mocks base method. +func (m *MockFileTransfer) AskInfoFile(conn fileSync.ShosetConn, fileState fileSync.FileState) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "AskInfoFile", conn, fileState) +} + +// AskInfoFile indicates an expected call of AskInfoFile. +func (mr *MockFileTransferMockRecorder) AskInfoFile(conn, fileState interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskInfoFile", reflect.TypeOf((*MockFileTransfer)(nil).AskInfoFile), conn, fileState) +} + +// Broadcast mocks base method. +func (m *MockFileTransfer) Broadcast(message *msg.FileMessage) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Broadcast", message) +} + +// Broadcast indicates an expected call of Broadcast. +func (mr *MockFileTransferMockRecorder) Broadcast(message interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Broadcast", reflect.TypeOf((*MockFileTransfer)(nil).Broadcast), message) +} + +// DecreaseMissingLength mocks base method. +func (m *MockFileTransfer) DecreaseMissingLength(length int) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DecreaseMissingLength", length) +} + +// DecreaseMissingLength indicates an expected call of DecreaseMissingLength. +func (mr *MockFileTransferMockRecorder) DecreaseMissingLength(length interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecreaseMissingLength", reflect.TypeOf((*MockFileTransfer)(nil).DecreaseMissingLength), length) +} + +// DeleteLeecher mocks base method. +func (m *MockFileTransfer) DeleteLeecher(fileUUID string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DeleteLeecher", fileUUID) +} + +// DeleteLeecher indicates an expected call of DeleteLeecher. +func (mr *MockFileTransferMockRecorder) DeleteLeecher(fileUUID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteLeecher", reflect.TypeOf((*MockFileTransfer)(nil).DeleteLeecher), fileUUID) +} + +// GetLeecher mocks base method. +func (m *MockFileTransfer) GetLeecher(fileUUID string) *fileSync.FileLeecher { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLeecher", fileUUID) + ret0, _ := ret[0].(*fileSync.FileLeecher) + return ret0 +} + +// GetLeecher indicates an expected call of GetLeecher. +func (mr *MockFileTransferMockRecorder) GetLeecher(fileUUID interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLeecher", reflect.TypeOf((*MockFileTransfer)(nil).GetLeecher), fileUUID) +} + +// GetLibrary mocks base method. +func (m *MockFileTransfer) GetLibrary() fileSync.FileLibrary { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLibrary") + ret0, _ := ret[0].(fileSync.FileLibrary) + return ret0 +} + +// GetLibrary indicates an expected call of GetLibrary. +func (mr *MockFileTransferMockRecorder) GetLibrary() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLibrary", reflect.TypeOf((*MockFileTransfer)(nil).GetLibrary)) +} + +// GetLogger mocks base method. +func (m *MockFileTransfer) GetLogger() *zerolog.Logger { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLogger") + ret0, _ := ret[0].(*zerolog.Logger) + return ret0 +} + +// GetLogger indicates an expected call of GetLogger. +func (mr *MockFileTransferMockRecorder) GetLogger() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLogger", reflect.TypeOf((*MockFileTransfer)(nil).GetLogger)) +} + +// GetMissingLength mocks base method. +func (m *MockFileTransfer) GetMissingLength() int64 { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetMissingLength") + ret0, _ := ret[0].(int64) + return ret0 +} + +// GetMissingLength indicates an expected call of GetMissingLength. +func (mr *MockFileTransferMockRecorder) GetMissingLength() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMissingLength", reflect.TypeOf((*MockFileTransfer)(nil).GetMissingLength)) +} + +// GetReceiveQueue mocks base method. +func (m *MockFileTransfer) GetReceiveQueue(conn *fileSync.ShosetConn) *fileSync.MessageQueue { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetReceiveQueue", conn) + ret0, _ := ret[0].(*fileSync.MessageQueue) + return ret0 +} + +// GetReceiveQueue indicates an expected call of GetReceiveQueue. +func (mr *MockFileTransferMockRecorder) GetReceiveQueue(conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReceiveQueue", reflect.TypeOf((*MockFileTransfer)(nil).GetReceiveQueue), conn) +} + +// HandleReceiveMessage mocks base method. +func (m *MockFileTransfer) HandleReceiveMessage(messageQueue *fileSync.MessageQueue, conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "HandleReceiveMessage", messageQueue, conn) +} + +// HandleReceiveMessage indicates an expected call of HandleReceiveMessage. +func (mr *MockFileTransferMockRecorder) HandleReceiveMessage(messageQueue, conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleReceiveMessage", reflect.TypeOf((*MockFileTransfer)(nil).HandleReceiveMessage), messageQueue, conn) +} + +// HandleReceiveMessageFromQueue mocks base method. +func (m *MockFileTransfer) HandleReceiveMessageFromQueue(fileMessage msg.FileMessage, c fileSync.ShosetConn) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HandleReceiveMessageFromQueue", fileMessage, c) + ret0, _ := ret[0].(error) + return ret0 +} + +// HandleReceiveMessageFromQueue indicates an expected call of HandleReceiveMessageFromQueue. +func (mr *MockFileTransferMockRecorder) HandleReceiveMessageFromQueue(fileMessage, c interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleReceiveMessageFromQueue", reflect.TypeOf((*MockFileTransfer)(nil).HandleReceiveMessageFromQueue), fileMessage, c) +} + +// HandleSendMessage mocks base method. +func (m *MockFileTransfer) HandleSendMessage(messageQueue *fileSync.MessageQueue, conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "HandleSendMessage", messageQueue, conn) +} + +// HandleSendMessage indicates an expected call of HandleSendMessage. +func (mr *MockFileTransferMockRecorder) HandleSendMessage(messageQueue, conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleSendMessage", reflect.TypeOf((*MockFileTransfer)(nil).HandleSendMessage), messageQueue, conn) +} + +// HandleSendMessageFromQueue mocks base method. +func (m *MockFileTransfer) HandleSendMessageFromQueue(fileMessage msg.FileMessage, c fileSync.ShosetConn) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "HandleSendMessageFromQueue", fileMessage, c) + ret0, _ := ret[0].(error) + return ret0 +} + +// HandleSendMessageFromQueue indicates an expected call of HandleSendMessageFromQueue. +func (mr *MockFileTransferMockRecorder) HandleSendMessageFromQueue(fileMessage, c interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleSendMessageFromQueue", reflect.TypeOf((*MockFileTransfer)(nil).HandleSendMessageFromQueue), fileMessage, c) +} + +// Init mocks base method. +func (m *MockFileTransfer) Init(library fileSync.FileLibrary, logger zerolog.Logger, userMessageQueue *msg.Queue, broadcast func(*msg.FileMessage)) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Init", library, logger, userMessageQueue, broadcast) +} + +// Init indicates an expected call of Init. +func (mr *MockFileTransferMockRecorder) Init(library, logger, userMessageQueue, broadcast interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Init", reflect.TypeOf((*MockFileTransfer)(nil).Init), library, logger, userMessageQueue, broadcast) +} + +// InitLeecher mocks base method. +func (m *MockFileTransfer) InitLeecher(syncFile fileSync.SyncFile, conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "InitLeecher", syncFile, conn) +} + +// InitLeecher indicates an expected call of InitLeecher. +func (mr *MockFileTransferMockRecorder) InitLeecher(syncFile, conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InitLeecher", reflect.TypeOf((*MockFileTransfer)(nil).InitLeecher), syncFile, conn) +} + +// IsAuthorised mocks base method. +func (m *MockFileTransfer) IsAuthorised(conn fileSync.ShosetConn) bool { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "IsAuthorised", conn) + ret0, _ := ret[0].(bool) + return ret0 +} + +// IsAuthorised indicates an expected call of IsAuthorised. +func (mr *MockFileTransferMockRecorder) IsAuthorised(conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsAuthorised", reflect.TypeOf((*MockFileTransfer)(nil).IsAuthorised), conn) +} + +// ReceiveAskBitfieldMessage mocks base method. +func (m *MockFileTransfer) ReceiveAskBitfieldMessage(conn fileSync.ShosetConn, message *msg.FileMessage) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReceiveAskBitfieldMessage", conn, message) +} + +// ReceiveAskBitfieldMessage indicates an expected call of ReceiveAskBitfieldMessage. +func (mr *MockFileTransferMockRecorder) ReceiveAskBitfieldMessage(conn, message interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveAskBitfieldMessage", reflect.TypeOf((*MockFileTransfer)(nil).ReceiveAskBitfieldMessage), conn, message) +} + +// ReceiveAskChunk mocks base method. +func (m *MockFileTransfer) ReceiveAskChunk(conn fileSync.ShosetConn, message *msg.FileMessage) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReceiveAskChunk", conn, message) + ret0, _ := ret[0].(error) + return ret0 +} + +// ReceiveAskChunk indicates an expected call of ReceiveAskChunk. +func (mr *MockFileTransferMockRecorder) ReceiveAskChunk(conn, message interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveAskChunk", reflect.TypeOf((*MockFileTransfer)(nil).ReceiveAskChunk), conn, message) +} + +// ReceiveAskInfoMessage mocks base method. +func (m *MockFileTransfer) ReceiveAskInfoMessage(conn fileSync.ShosetConn, message *msg.FileMessage) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReceiveAskInfoMessage", conn, message) +} + +// ReceiveAskInfoMessage indicates an expected call of ReceiveAskInfoMessage. +func (mr *MockFileTransferMockRecorder) ReceiveAskInfoMessage(conn, message interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveAskInfoMessage", reflect.TypeOf((*MockFileTransfer)(nil).ReceiveAskInfoMessage), conn, message) +} + +// ReceiveAuthorisedMessage mocks base method. +func (m *MockFileTransfer) ReceiveAuthorisedMessage(conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReceiveAuthorisedMessage", conn) +} + +// ReceiveAuthorisedMessage indicates an expected call of ReceiveAuthorisedMessage. +func (mr *MockFileTransferMockRecorder) ReceiveAuthorisedMessage(conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveAuthorisedMessage", reflect.TypeOf((*MockFileTransfer)(nil).ReceiveAuthorisedMessage), conn) +} + +// ReceiveBitfieldMessage mocks base method. +func (m *MockFileTransfer) ReceiveBitfieldMessage(conn fileSync.ShosetConn, message *msg.FileMessage) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReceiveBitfieldMessage", conn, message) +} + +// ReceiveBitfieldMessage indicates an expected call of ReceiveBitfieldMessage. +func (mr *MockFileTransferMockRecorder) ReceiveBitfieldMessage(conn, message interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveBitfieldMessage", reflect.TypeOf((*MockFileTransfer)(nil).ReceiveBitfieldMessage), conn, message) +} + +// ReceiveChunk mocks base method. +func (m *MockFileTransfer) ReceiveChunk(conn fileSync.ShosetConn, message *msg.FileMessage) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ReceiveChunk", conn, message) + ret0, _ := ret[0].(error) + return ret0 +} + +// ReceiveChunk indicates an expected call of ReceiveChunk. +func (mr *MockFileTransferMockRecorder) ReceiveChunk(conn, message interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveChunk", reflect.TypeOf((*MockFileTransfer)(nil).ReceiveChunk), conn, message) +} + +// ReceiveCongestionMessage mocks base method. +func (m *MockFileTransfer) ReceiveCongestionMessage(conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReceiveCongestionMessage", conn) +} + +// ReceiveCongestionMessage indicates an expected call of ReceiveCongestionMessage. +func (mr *MockFileTransferMockRecorder) ReceiveCongestionMessage(conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveCongestionMessage", reflect.TypeOf((*MockFileTransfer)(nil).ReceiveCongestionMessage), conn) +} + +// ReceiveHaveMessage mocks base method. +func (m *MockFileTransfer) ReceiveHaveMessage(conn fileSync.ShosetConn, message *msg.FileMessage) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReceiveHaveMessage", conn, message) +} + +// ReceiveHaveMessage indicates an expected call of ReceiveHaveMessage. +func (mr *MockFileTransferMockRecorder) ReceiveHaveMessage(conn, message interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveHaveMessage", reflect.TypeOf((*MockFileTransfer)(nil).ReceiveHaveMessage), conn, message) +} + +// ReceiveInterestedMessage mocks base method. +func (m *MockFileTransfer) ReceiveInterestedMessage(conn fileSync.ShosetConn, message *msg.FileMessage) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReceiveInterestedMessage", conn, message) +} + +// ReceiveInterestedMessage indicates an expected call of ReceiveInterestedMessage. +func (mr *MockFileTransferMockRecorder) ReceiveInterestedMessage(conn, message interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveInterestedMessage", reflect.TypeOf((*MockFileTransfer)(nil).ReceiveInterestedMessage), conn, message) +} + +// ReceiveMessage mocks base method. +func (m *MockFileTransfer) ReceiveMessage(message *msg.FileMessage, conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReceiveMessage", message, conn) +} + +// ReceiveMessage indicates an expected call of ReceiveMessage. +func (mr *MockFileTransferMockRecorder) ReceiveMessage(message, conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveMessage", reflect.TypeOf((*MockFileTransfer)(nil).ReceiveMessage), message, conn) +} + +// ReceiveUnauthorisedMessage mocks base method. +func (m *MockFileTransfer) ReceiveUnauthorisedMessage(conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "ReceiveUnauthorisedMessage", conn) +} + +// ReceiveUnauthorisedMessage indicates an expected call of ReceiveUnauthorisedMessage. +func (mr *MockFileTransferMockRecorder) ReceiveUnauthorisedMessage(conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveUnauthorisedMessage", reflect.TypeOf((*MockFileTransfer)(nil).ReceiveUnauthorisedMessage), conn) +} + +// RemoveConn mocks base method. +func (m *MockFileTransfer) RemoveConn(conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RemoveConn", conn) +} + +// RemoveConn indicates an expected call of RemoveConn. +func (mr *MockFileTransferMockRecorder) RemoveConn(conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveConn", reflect.TypeOf((*MockFileTransfer)(nil).RemoveConn), conn) +} + +// RemoveFileLeecher mocks base method. +func (m *MockFileTransfer) RemoveFileLeecher(fileLeecher *fileSync.FileLeecher) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RemoveFileLeecher", fileLeecher) +} + +// RemoveFileLeecher indicates an expected call of RemoveFileLeecher. +func (mr *MockFileTransferMockRecorder) RemoveFileLeecher(fileLeecher interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveFileLeecher", reflect.TypeOf((*MockFileTransfer)(nil).RemoveFileLeecher), fileLeecher) +} + +// RemoveFileSeeder mocks base method. +func (m *MockFileTransfer) RemoveFileSeeder(fileSeeder *fileSync.FileSeeder) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RemoveFileSeeder", fileSeeder) +} + +// RemoveFileSeeder indicates an expected call of RemoveFileSeeder. +func (mr *MockFileTransferMockRecorder) RemoveFileSeeder(fileSeeder interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveFileSeeder", reflect.TypeOf((*MockFileTransfer)(nil).RemoveFileSeeder), fileSeeder) +} + +// SendAuthorisedMessage mocks base method. +func (m *MockFileTransfer) SendAuthorisedMessage(conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SendAuthorisedMessage", conn) +} + +// SendAuthorisedMessage indicates an expected call of SendAuthorisedMessage. +func (mr *MockFileTransferMockRecorder) SendAuthorisedMessage(conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendAuthorisedMessage", reflect.TypeOf((*MockFileTransfer)(nil).SendAuthorisedMessage), conn) +} + +// SendCongestionMessage mocks base method. +func (m *MockFileTransfer) SendCongestionMessage(conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SendCongestionMessage", conn) +} + +// SendCongestionMessage indicates an expected call of SendCongestionMessage. +func (mr *MockFileTransferMockRecorder) SendCongestionMessage(conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendCongestionMessage", reflect.TypeOf((*MockFileTransfer)(nil).SendCongestionMessage), conn) +} + +// SendMessage mocks base method. +func (m *MockFileTransfer) SendMessage(message msg.FileMessage, conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SendMessage", message, conn) +} + +// SendMessage indicates an expected call of SendMessage. +func (mr *MockFileTransferMockRecorder) SendMessage(message, conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMessage", reflect.TypeOf((*MockFileTransfer)(nil).SendMessage), message, conn) +} + +// SendUnauthorisedMessage mocks base method. +func (m *MockFileTransfer) SendUnauthorisedMessage(conn fileSync.ShosetConn) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SendUnauthorisedMessage", conn) +} + +// SendUnauthorisedMessage indicates an expected call of SendUnauthorisedMessage. +func (mr *MockFileTransferMockRecorder) SendUnauthorisedMessage(conn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendUnauthorisedMessage", reflect.TypeOf((*MockFileTransfer)(nil).SendUnauthorisedMessage), conn) +} + +// SetExternalCommands mocks base method. +func (m *MockFileTransfer) SetExternalCommands(ec *fileSync.ExternalCommands) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetExternalCommands", ec) +} + +// SetExternalCommands indicates an expected call of SetExternalCommands. +func (mr *MockFileTransferMockRecorder) SetExternalCommands(ec interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetExternalCommands", reflect.TypeOf((*MockFileTransfer)(nil).SetExternalCommands), ec) +} + +// SetNbConn mocks base method. +func (m *MockFileTransfer) SetNbConn(nbConn int) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetNbConn", nbConn) +} + +// SetNbConn indicates an expected call of SetNbConn. +func (mr *MockFileTransferMockRecorder) SetNbConn(nbConn interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetNbConn", reflect.TypeOf((*MockFileTransfer)(nil).SetNbConn), nbConn) +} + +// UserPush mocks base method. +func (m *MockFileTransfer) UserPush(message *msg.FileMessage) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UserPush", message) +} + +// UserPush indicates an expected call of UserPush. +func (mr *MockFileTransferMockRecorder) UserPush(message interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserPush", reflect.TypeOf((*MockFileTransfer)(nil).UserPush), message) +} + +// WriteRecords mocks base method. +func (m *MockFileTransfer) WriteRecords() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "WriteRecords") +} + +// WriteRecords indicates an expected call of WriteRecords. +func (mr *MockFileTransferMockRecorder) WriteRecords() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteRecords", reflect.TypeOf((*MockFileTransfer)(nil).WriteRecords)) +} diff --git a/test/multiProcesses_A.sh b/test/multiProcesses_A.sh new file mode 100644 index 0000000..95d395b --- /dev/null +++ b/test/multiProcesses_A.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +#binary testNumber Lname receiver sender destination relaunch + +./bin/shoset_build 5 A 1 0 rien 0 & +P=$! + +#Kill and restart +sleep 35 + +kill $P + +sleep 1 + +./bin/shoset_build 5 A 1 0 rien 1 & + +wait diff --git a/test/multiProcesses_B.sh b/test/multiProcesses_B.sh new file mode 100644 index 0000000..8fa2de6 --- /dev/null +++ b/test/multiProcesses_B.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +#binary testNumber Lname receiver sender destination relaunch + +sleep 1 + +./bin/shoset_build 5 B 0 0 rien 0 & +P=$! + +#Kill and restart +sleep 30 + +kill $P + +sleep 1 + +./bin/shoset_build 5 B 0 0 rien 1 & + +wait \ No newline at end of file diff --git a/test/multiProcesses_C.sh b/test/multiProcesses_C.sh new file mode 100644 index 0000000..9694bdb --- /dev/null +++ b/test/multiProcesses_C.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +#binary testNumber Lname receiver sender destination relaunch + +sleep 2 + +./bin/shoset_build 5 C 0 0 rien 0 & +P=$! + +#Kill and restart +sleep 25 + +kill $P + +sleep 1 + +./bin/shoset_build 5 C 0 0 rien 1 & + +wait \ No newline at end of file diff --git a/test/multiProcesses_D.sh b/test/multiProcesses_D.sh new file mode 100644 index 0000000..a994f7e --- /dev/null +++ b/test/multiProcesses_D.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +#binary testNumber Lname receiver sender destination relaunch + +sleep 3 + +./bin/shoset_build 5 D 0 0 rien 0 & +P=$! + +#Kill and restart +sleep 20 + +kill $P + +sleep 1 + +./bin/shoset_build 5 D 0 0 rien 1 & + +wait \ No newline at end of file diff --git a/test/multiProcesses_E.sh b/test/multiProcesses_E.sh new file mode 100644 index 0000000..a64298d --- /dev/null +++ b/test/multiProcesses_E.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +#binary testNumber Lname receiver sender destination relaunch + +sleep 4 + +./bin/shoset_build 5 E 0 1 A 0 & +P=$! + +#Kill and restart +sleep 15 + +kill $P + +sleep 1 + +./bin/shoset_build 5 E 0 1 A 1 & + +wait \ No newline at end of file diff --git a/test/multiProcesses_prep.sh b/test/multiProcesses_prep.sh new file mode 100644 index 0000000..c10ff04 --- /dev/null +++ b/test/multiProcesses_prep.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# To anable attaching to process : /proc/sys/kernel/yama/ptrace_scope to 0 +#code -n /proc/sys/kernel/yama/ptrace_scope + +#-gcflags=all="-N -l" Disable optimizations for debugging + +go build -v -o ./bin/shoset_build -race -gcflags=all="-N -l" ./test/test.go + +rm -rf ~/.shoset + +rm -rf ./profiler/* + +wait \ No newline at end of file diff --git a/test/old_test/oldTest.go b/test/old_test/oldTest.go new file mode 100644 index 0000000..7d38d0d --- /dev/null +++ b/test/old_test/oldTest.go @@ -0,0 +1,818 @@ +package oldTest + +// This file haven't been touched in a while, there no garanty that it still works with the current version o the code. + +import ( + "context" + "fmt" + "time" + + // "os" + // "log" + + "github.com/ditrit/shoset" + utilsForTest "github.com/ditrit/shoset/test/utils_for_test" +) + +// func shosetClient(logicalName, ShosetType, address string) { +// c := shoset.NewShoset(logicalName, ShosetType) +// c.Protocol(address, "link") + +// go func() { +// for { +// time.Sleep(time.Second * time.Duration(5)) +// } +// }() +// /* +// go func() { +// command := msg.NewCommand("orchestrator", "deploy", "{\"appli\": \"toto\"}") +// c.SendCommand(command) +// event := msg.NewEvent("bus", "coucou", "ok") +// c.SendEvent(event) + +// events := msg.NewIterator(c.qEvents) +// defer events.Close() +// rec := c.WaitEvent(events, "bus", "started", 20) +// if rec != nil { +// fmt.Println(">Received Event: \n%#v\n", *rec) +// } else { +// fmt.Print("Timeout expired !") +// } +// events2 := msg.NewIterator(c.qEvents) +// defer events.Close() +// rec2 := c.WaitEvent(events2, "bus", "starting", 20) +// if rec2 != nil { +// fmt.Println(">Received Event 2: \n%#v\n", *rec2) +// } else { +// fmt.Print("Timeout expired 2 !") +// } +// }() + +// */ +// <-c.Done +// } + +// func shosetServer(logicalName, ShosetType, address string) { +// s := shoset.NewShoset(logicalName, ShosetType) +// err := s.Bind(address) + +// if err != nil { +// fmt.Println("Gandalf server socket can not be created") +// } + +// go func() { +// for { +// time.Sleep(time.Second * time.Duration(5)) +// } +// }() +// /* +// go func() { +// time.Sleep(time.Second * time.Duration(5)) +// event := msg.NewEvent("bus", "starting", "ok") +// s.SendEvent(event) +// time.Sleep(time.Millisecond * time.Duration(200)) +// event = msg.NewEvent("bus", "started", "ok") +// s.SendEvent(event) +// command := msg.NewCommand("bus", "register", "{\"topic\": \"toto\"}") +// s.SendCommand(command) +// reply := msg.NewReply(command, "success", "OK") +// s.SendReply(reply) +// }() +// */ +// <-s.Done +// } + +// func shosetTest() { +// ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) +// defer cancel() + +// c1 := shoset.NewShoset("c", "c") +// c1.Bind("localhost:8301") + +// c2 := shoset.NewShoset("c", "c") +// c2.Bind("localhost:8302") + +// c3 := shoset.NewShoset("c", "c") +// c3.Bind("localhost:8303") + +// d1 := shoset.NewShoset("d", "a") +// d1.Bind("localhost:8401") + +// d2 := shoset.NewShoset("d", "a") +// d2.Bind("localhost:8402") + +// b1 := shoset.NewShoset("b", "c") +// b1.Bind("localhost:8201") +// b1.Protocol("localhost:8302", "link") +// b1.Protocol("localhost:8301", "link") +// b1.Protocol("localhost:8303", "link") +// b1.Protocol("localhost:8401", "link") +// b1.Protocol("localhost:8402", "link") + +// a1 := shoset.NewShoset("a", "c") +// a1.Bind("localhost:8101") +// a1.Protocol("localhost:8201", "link") + +// b2 := shoset.NewShoset("b", "c") +// b2.Bind("localhost:8202") +// b2.Protocol("localhost:8301", "link") + +// b3 := shoset.NewShoset("b", "c") +// b3.Bind("localhost:8203") +// b3.Protocol("localhost:8303", "link") + +// a2 := shoset.NewShoset("a", "c") +// a2.Bind("localhost:8102") +// a2.Protocol("localhost:8202", "link") + +// time.Sleep(time.Second * time.Duration(1)) +// fmt.Println("a1 : ", a1.String()) +// fmt.Println("a2 : ", a2.String()) +// fmt.Println("b1 : ", b1.String()) +// fmt.Println("b2 : ", b2.String()) +// fmt.Println("b3 : ", b3.String()) +// fmt.Println("c1 : ", c1.String()) +// fmt.Println("c2 : ", c2.String()) +// fmt.Println("c3 : ", c3.String()) +// fmt.Println("d1 : ", d1.String()) +// fmt.Println("d2 : ", d2.String()) +// <-done +// } + +// func shosetTestEtoile() { +// ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) +// defer cancel() + +// cl1 := shoset.NewShoset("cl", "cl") +// cl1.Bind("localhost:8001") + +// cl2 := shoset.NewShoset("cl", "cl") +// cl2.Bind("localhost:8002") +// cl2.Protocol("localhost:8001", "join") +// cl3 := shoset.NewShoset("cl", "cl") +// cl3.Bind("localhost:8003") +// cl3.Protocol("localhost:8002", "join") + +// cl4 := shoset.NewShoset("cl", "cl") +// cl4.Bind("localhost:8004") +// cl4.Protocol("localhost:8001", "join") + +// cl5 := shoset.NewShoset("cl", "cl") +// cl5.Bind("localhost:8005") +// cl5.Protocol("localhost:8001", "join") + +// aga1 := shoset.NewShoset("aga", "a") +// aga1.Bind("localhost:8111") +// aga1.Protocol("localhost:8001", "link") +// aga2 := shoset.NewShoset("aga", "a") +// aga2.Bind("localhost:8112") +// aga2.Protocol("localhost:8005", "link") + +// agb1 := shoset.NewShoset("agb", "a") +// agb1.Bind("localhost:8121") +// agb1.Protocol("localhost:8002", "link") +// agb2 := shoset.NewShoset("agb", "a") +// agb2.Bind("localhost:8122") +// agb2.Protocol("localhost:8003", "link") + +// time.Sleep(time.Second * time.Duration(2)) + +// Ca1 := shoset.NewShoset("Ca", "c") +// Ca1.Bind("localhost:8211") +// Ca1.Protocol("localhost:8111", "link") +// Ca2 := shoset.NewShoset("Ca", "c") +// Ca2.Bind("localhost:8212") +// Ca2.Protocol("localhost:8111", "link") +// Ca3 := shoset.NewShoset("Ca", "c") +// Ca3.Bind("localhost:8213") +// Ca3.Protocol("localhost:8111", "link") + +// Cb1 := shoset.NewShoset("Cb", "c") +// Cb1.Bind("localhost:8221") +// Cb1.Protocol("localhost:8112", "link") +// Cb2 := shoset.NewShoset("Cb", "c") +// Cb2.Bind("localhost:8222") +// Cb2.Protocol("localhost:8112", "link") + +// Cc1 := shoset.NewShoset("Cc", "c") +// Cc1.Bind("localhost:8231") +// Cc1.Protocol("localhost:8111", "link") +// Cc2 := shoset.NewShoset("Cc", "c") +// Cc2.Bind("localhost:8232") +// Cc2.Protocol("localhost:8111", "link") + +// Cd1 := shoset.NewShoset("Cd", "c") +// Cd1.Bind("localhost:8241") +// Cd1.Protocol("localhost:8111", "link") +// Cd2 := shoset.NewShoset("Cd", "c") +// Cd2.Bind("localhost:8242") +// Cd2.Protocol("localhost:8112", "link") + +// Ce1 := shoset.NewShoset("Ce", "c") +// Ce1.Bind("localhost:8251") +// Ce1.Protocol("localhost:8122", "link") +// Ce2 := shoset.NewShoset("Ce", "c") +// Ce2.Bind("localhost:8252") +// Ce2.Protocol("localhost:8122", "link") + +// Cf1 := shoset.NewShoset("Cf", "c") +// Cf1.Bind("localhost:8261") +// Cf1.Protocol("localhost:8121", "link") +// Cf2 := shoset.NewShoset("Cg", "c") +// Cf2.Bind("localhost:8262") +// Cf2.Protocol("localhost:8121", "link") + +// Cg1 := shoset.NewShoset("Cg", "c") +// Cg1.Bind("localhost:8271") +// Cg1.Protocol("localhost:8121", "link") +// Cg2 := shoset.NewShoset("Cg", "c") +// Cg2.Bind("localhost:8272") +// Cg2.Protocol("localhost:8122", "link") + +// Ch1 := shoset.NewShoset("Ch", "c") +// Ch1.Bind("localhost:8281") +// Ch1.Protocol("localhost:8111", "link") + +// time.Sleep(time.Second * time.Duration(2)) +// fmt.Println("cl1 : ", cl2.String()) +// fmt.Println("cl2 : ", cl2.String()) +// fmt.Println("cl3 : ", cl3.String()) +// fmt.Println("cl4 : ", cl4.String()) +// fmt.Println("cl5 : ", cl5.String()) + +// fmt.Println("aga1 : ", aga1.String()) +// fmt.Println("aga2 : ", aga2.String()) + +// fmt.Println("agb1 : ", agb1.String()) +// fmt.Println("agb2 : ", agb2.String()) + +// fmt.Println("Ca1 : ", Ca1.String()) +// fmt.Println("Ca2 : ", Ca2.String()) +// fmt.Println("Ca3 : ", Ca3.String()) + +// fmt.Println("Cb1 : ", Cb1.String()) +// fmt.Println("Cb2 : ", Cb2.String()) + +// fmt.Println("Cc1 : ", Cc1.String()) +// fmt.Println("Cc2 : ", Cc2.String()) + +// fmt.Println("Cd1 : ", Cd1.String()) +// fmt.Println("Cd2 : ", Cd2.String()) + +// fmt.Println("Ce1 : ", Ce1.String()) +// fmt.Println("Ce2 : ", Ce2.String()) + +// fmt.Println("Cf1 : ", Cf1.String()) +// fmt.Println("Cf2 : ", Cf2.String()) + +// fmt.Println("Cg1 : ", Cg1.String()) +// fmt.Println("Cg2 : ", Cg2.String()) + +// fmt.Println("Ch1 : ", Ch1.String()) + +// <-done +// } + +// func testQueue() { +// ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) +// defer cancel() +// +// /* // First let's make 2 sockets talk each other +// C1 := shoset.NewShoset("C1", "c") +// C1.Bind("localhost:8261") +// C1.Protocol("localhost:8262","link") + +// C2 := shoset.NewShoset("C2", "cl") +// C2.Bind("localhost:8262") +// C2.Protocol("localhost:8261","link") + +// // Let's check for sockets connections +// time.Sleep(time.Second * time.Duration(1)) + +// fmt.Println("C1 : ", C1.String()) +// fmt.Println("C2 : ", C2.String()) + +// // Make C1 send a message to C2 +// socket := C1.GetConnByAddr(C2.GetBindAddr()) +// m := msg.NewCommand("test", "test", "content") +// m.Timeout = 10000 +// fmt.Println("Message Pushed: %+v\n", *m) +// socket.SendMessage(m) + +// // Let's dump C2 queue for cmd msg +// time.Sleep(time.Second * time.Duration(1)) +// cell := C2.FQueue("cmd").First() +// fmt.Println("Cell in queue: %+v\n", *cell) +// */<-done +// } + +func SimpleCluster() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + cl1 := shoset.NewShoset("cl", "cl") + cl1.Bind("localhost:8001") //we take the port 8001 for our first socket + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", cl1) + }) +} + +func SimpleAgregator() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + aga1 := shoset.NewShoset("aga", "a") // agregateur + aga1.Protocol("localhost:8111", "localhost:8001", "link") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", aga1) + }) +} + +func SimpleConnector() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + Ca1 := shoset.NewShoset("Ca", "c") // agregateur + Ca1.Protocol("localhost:8211", "localhost:8111", "link") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", Ca1) + }) +} + +func SimplesimpleConnector() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + Ca1 := shoset.NewShoset("Ca", "c") // agregateur + Ca1.Bind("localhost:8211") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", Ca1) + }) +} + +func TestJoin1() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cl1 := shoset.NewShoset("cl", "cl") + cl1.Bind("localhost:8001") + + cl2 := shoset.NewShoset("cl", "cl") // always "cl" "cl" for gandalf + cl2.Protocol("localhost:8002", "localhost:8001", "join") // we join it to our first socket + + cl3 := shoset.NewShoset("cl", "cl") + cl3.Protocol("localhost:8003", "localhost:8001", "join") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", cl1) + fmt.Println("\ncl : ", cl2) + fmt.Println("\ncl : ", cl3) + }) +} + +func TestJoin2() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cl2 := shoset.NewShoset("cl", "cl") // always "cl" "cl" for gandalf + cl2.Protocol("localhost:8002", "localhost:8001", "join") // we join it to our first socket + + cl3 := shoset.NewShoset("cl", "cl") + cl3.Protocol("localhost:8003", "localhost:8001", "join") + // cl3.Protocol("localhost:8002", "join") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", cl2) + fmt.Println("\ncl : ", cl3) + }) +} + +func TestJoin3(ctx context.Context, done context.CancelFunc) { + cl1 := shoset.NewShoset("cl", "cl") // cluster + cl1.InitPKI("localhost:8001") + + cl2 := shoset.NewShoset("cl", "cl") + cl2.Protocol("localhost:8002", "localhost:8001", "join") + + cl3 := shoset.NewShoset("cl", "cl") + cl3.Protocol("localhost:8003", "localhost:8002", "join") + + cl4 := shoset.NewShoset("c", "c") + cl4.Protocol("localhost:8004", "localhost:8001", "link") + + cl5 := shoset.NewShoset("cl", "cl") + cl5.Protocol("localhost:8005", "localhost:8003", "join") + + cl6 := shoset.NewShoset("c", "c") + cl6.Protocol("localhost:8006", "localhost:8002", "link") + + time.Sleep(time.Second * time.Duration(2)) + cl2.Protocol("localhost:8002", "localhost:8003", "bye") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Printf("%s: %v", cl1.GetLogicalName(), cl1) + fmt.Printf("%s: %v", cl2.GetLogicalName(), cl2) + fmt.Printf("%s: %v", cl3.GetLogicalName(), cl3) + fmt.Printf("%s: %v", cl4.GetLogicalName(), cl4) + fmt.Printf("%s: %v", cl5.GetLogicalName(), cl5) + fmt.Printf("%s: %v", cl6.GetLogicalName(), cl6) + done() + }) +} + +func TestJoin4() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cl2 := shoset.NewShoset("cl", "cl") // always "cl" "cl" for gandalf + cl2.Protocol("localhost:8002", "localhost:8001", "join") // we join it to our first socket + + cl3 := shoset.NewShoset("cl", "cl") + cl3.Protocol("localhost:8003", "localhost:8002", "join") + + cl4 := shoset.NewShoset("cl", "cl") + cl4.Protocol("localhost:8004", "localhost:8001", "join") // we join it to our first socket + + cl5 := shoset.NewShoset("cl", "cl") + cl5.Protocol("localhost:8005", "localhost:8004", "join") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", cl2) + fmt.Println("\ncl : ", cl3) + fmt.Println("\ncl : ", cl4) + fmt.Println("\ncl : ", cl5) + }) +} + +// func testJoin() { +// ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) +// defer cancel() + +// cl2 := shoset.NewShoset("cl", "cl") // always "cl" "cl" for gandalf +// fmt.Println("\ncl : ", cl2) +// cl2.Bind("localhost:8002") //we take the port 8002 for our first socket +// cl2.Protocol("localhost:8001", "join") // we join it to our first socket + +// cl3 := shoset.NewShoset("cl", "cl") +// cl3.Bind("localhost:8003") +// cl3.Protocol("localhost:8001", "join") +// cl3.Protocol("localhost:8002", "join") + +// cl4 := shoset.NewShoset("cl", "cl") +// cl4.Bind("localhost:8004") +// cl4.Protocol("localhost:8002", "join") // we join it to our first socket + +// for { +// time.Sleep(time.Second * time.Duration(2)) +// fmt.Println("\ncl : ", cl2) +// fmt.Println("\ncl : ", cl3) +// fmt.Println("\ncl : ", cl4) +// } + +// <-done +// } + +// func testLink() { +// ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) +// defer cancel() + +// cl1 := shoset.NewShoset("cl", "cl") // cluster +// cl1.Bind("localhost:8001") + +// cl2 := shoset.NewShoset("cl", "cl") +// cl2.Bind("localhost:8002") +// cl2.Protocol("localhost:8001","join") + +// cl3 := shoset.NewShoset("cl", "cl") +// cl3.Bind("localhost:8003") +// cl3.Protocol("localhost:8002","join") + +// aga1 := shoset.NewShoset("aga", "a") // agregateur +// aga1.Bind("localhost:8111") +// aga1.Protocol("localhost:8001","link") + +// aga2 := shoset.NewShoset("aga", "a") // agregateur +// aga2.Bind("localhost:8112") +// aga2.Protocol("localhost:8002","link") + +// Ca1 := shoset.NewShoset("Ca", "c") //connecteur +// Ca1.Bind("localhost:8211") +// Ca1.Protocol("localhost:8111","link") + +// time.Sleep(time.Second * time.Duration(3)) +// aga3 := shoset.NewShoset("aga", "a") // agregateur +// aga3.Bind("localhost:8113") +// aga3.Protocol("localhost:8002","link") + +// Ca2 := shoset.NewShoset("Ca", "c") //connecteur +// Ca2.Bind("localhost:8212") +// Ca2.Protocol("localhost:8113","link") + +// for { +// fmt.Println("\ncl : ", cl1) +// fmt.Println("\ncl : ", cl2) +// // fmt.Println("\n", cl2.ConnsByLname) +// fmt.Println("\ncl : ", cl3) +// // fmt.Println("\nag : ", aga1) +// fmt.Println("\nag : ", aga2) +// fmt.Println("\nag : ", aga3) +// fmt.Println("\nca : ", Ca1) +// fmt.Println("\nca : ", Ca2) +// time.Sleep(time.Second * time.Duration(3)) +// } + +// <-done +// } + +func TestLink1() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cl1 := shoset.NewShoset("cl", "cl") // cluster + cl1.Bind("localhost:8001") + + cl2 := shoset.NewShoset("cl", "cl") + cl2.Protocol("localhost:8002", "localhost:8001", "join") + + cl3 := shoset.NewShoset("cl", "cl") + cl3.Protocol("localhost:8003", "localhost:8002", "join") + + cl4 := shoset.NewShoset("cl", "cl") + cl4.Protocol("localhost:8004", "localhost:8001", "join") + + aga1 := shoset.NewShoset("aga", "a") // agregateur + aga1.Protocol("localhost:8111", "localhost:8001", "link") + + aga2 := shoset.NewShoset("aga", "a") // agregateur + aga2.Protocol("localhost:8112", "localhost:8001", "link") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", cl1) + fmt.Println("\ncl : ", cl2) + fmt.Println("\ncl : ", cl3) + fmt.Println("\ncl : ", cl4) + fmt.Println("\nag : ", aga1) + fmt.Println("\nag : ", aga2) + }) +} + +func TestLink2() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cl2 := shoset.NewShoset("cl", "cl") + cl2.Protocol("localhost:8002", "localhost:8001", "join") + + cl3 := shoset.NewShoset("cl", "cl") + cl3.Protocol("localhost:8003", "localhost:8002", "join") + + cl4 := shoset.NewShoset("cl", "cl") + cl4.Protocol("localhost:8004", "localhost:8001", "join") + + aga1 := shoset.NewShoset("aga", "a") // agregateur + aga1.Protocol("localhost:8111", "localhost:8001", "link") + + aga2 := shoset.NewShoset("aga", "a") // agregateur + aga2.Protocol("localhost:8112", "localhost:8001", "link") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", cl2) + fmt.Println("\ncl : ", cl3) + fmt.Println("\ncl : ", cl4) + fmt.Println("\nag : ", aga1) + fmt.Println("\nag : ", aga2) + }) +} + +func TestLink3() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cl1 := shoset.NewShoset("cl", "cl") // cluster + cl1.Bind("localhost:8001") + + cl2 := shoset.NewShoset("cl", "cl") + cl2.Protocol("localhost:8002", "localhost:8001", "join") + + cl3 := shoset.NewShoset("cl", "cl") + cl3.Protocol("localhost:8003", "localhost:8002", "join") + + cl4 := shoset.NewShoset("cl", "cl") + cl4.Protocol("localhost:8004", "localhost:8001", "join") + + aga1 := shoset.NewShoset("aga", "a") // agregateur + aga1.Protocol("localhost:8111", "localhost:8001", "link") + + aga2 := shoset.NewShoset("aga", "a") // agregateur + aga2.Protocol("localhost:8112", "localhost:8002", "link") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", cl1) + fmt.Println("\ncl : ", cl2) + fmt.Println("\ncl : ", cl3) + fmt.Println("\ncl : ", cl4) + fmt.Println("\nag : ", aga1) + fmt.Println("\nag : ", aga2) + }) +} + +func TestLink4() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cl2 := shoset.NewShoset("cl", "cl") + cl2.Protocol("localhost:8002", "localhost:8001", "join") + + cl3 := shoset.NewShoset("cl", "cl") + cl3.Protocol("localhost:8003", "localhost:8002", "join") + + cl4 := shoset.NewShoset("cl", "cl") + cl4.Protocol("localhost:8004", "localhost:8001", "join") + + aga1 := shoset.NewShoset("aga", "a") // agregateur + aga1.Protocol("localhost:8111", "localhost:8001", "link") + + aga2 := shoset.NewShoset("aga", "a") // agregateur + aga2.Protocol("localhost:8112", "localhost:8002", "link") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", cl2) + fmt.Println("\ncl : ", cl3) + fmt.Println("\ncl : ", cl4) + fmt.Println("\nag : ", aga1) + fmt.Println("\nag : ", aga2) + }) +} + +func TestLink5() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cl1 := shoset.NewShoset("cl", "cl") // cluster + cl1.Bind("localhost:8001") + + cl2 := shoset.NewShoset("cl", "cl") + cl2.Protocol("localhost:8002", "localhost:8001", "join") + + cl3 := shoset.NewShoset("cl", "cl") + cl3.Protocol("localhost:8003", "localhost:8002", "join") + + cl4 := shoset.NewShoset("cl", "cl") + cl4.Protocol("localhost:8004", "localhost:8001", "join") + + aga1 := shoset.NewShoset("aga", "a") // agregateur + aga1.Protocol("localhost:8111", "localhost:8001", "link") + + aga2 := shoset.NewShoset("aga", "a") // agregateur + aga2.Protocol("localhost:8112", "localhost:8002", "link") + + Ca1 := shoset.NewShoset("Ca", "c") //connecteur + Ca1.Protocol("localhost:8211", "localhost:8111", "link") + + Ca2 := shoset.NewShoset("Ca", "c") //connecteur + Ca2.Protocol("localhost:8212", "localhost:8112", "link") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", cl1) + fmt.Println("\ncl : ", cl2) + fmt.Println("\ncl : ", cl3) + fmt.Println("\ncl : ", cl4) + fmt.Println("\nag : ", aga1) + fmt.Println("\nag : ", aga2) + fmt.Println("\nca : ", Ca1) + fmt.Println("\nca : ", Ca2) + }) +} + +func TestLink6() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cl2 := shoset.NewShoset("cl", "cl") + cl2.Protocol("localhost:8002", "localhost:8001", "join") + + cl3 := shoset.NewShoset("cl", "cl") + cl3.Protocol("localhost:8003", "localhost:8002", "join") + + cl4 := shoset.NewShoset("cl", "cl") + cl4.Protocol("localhost:8004", "localhost:8001", "join") + + aga1 := shoset.NewShoset("aga", "a") // agregateur + aga1.Protocol("localhost:8111", "localhost:8001", "link") + + aga2 := shoset.NewShoset("aga", "a") // agregateur + aga2.Protocol("localhost:8112", "localhost:8002", "link") + + Ca1 := shoset.NewShoset("Ca", "c") //connecteur + Ca1.Protocol("localhost:8211", "localhost:8111", "link") + + Ca2 := shoset.NewShoset("Ca", "c") //connecteur + Ca2.Protocol("localhost:8212", "localhost:8112", "link") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", cl2) + fmt.Println("\ncl : ", cl3) + fmt.Println("\ncl : ", cl4) + fmt.Println("\nag : ", aga1) + fmt.Println("\nag : ", aga2) + fmt.Println("\nca : ", Ca1) + fmt.Println("\nca : ", Ca2) + }) +} + +func TestLink7() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cl1 := shoset.NewShoset("cl", "cl") // cluster + cl1.Bind("localhost:8001") + + cl2 := shoset.NewShoset("cl", "cl") + cl2.Protocol("localhost:8002", "localhost:8001", "join") + + cl3 := shoset.NewShoset("cl", "cl") + cl3.Protocol("localhost:8003", "localhost:8002", "join") + + cl4 := shoset.NewShoset("cl", "cl") + cl4.Protocol("localhost:8004", "localhost:8001", "join") + + aga2 := shoset.NewShoset("aga", "a") // agregateur + aga2.Protocol("localhost:8112", "localhost:8002", "link") + + Ca1 := shoset.NewShoset("Ca", "c") //connecteur + Ca1.Protocol("localhost:8211", "localhost:8111", "link") + + Ca2 := shoset.NewShoset("Ca", "c") //connecteur + Ca2.Protocol("localhost:8212", "localhost:8112", "link") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("\ncl : ", cl1) + fmt.Println("\ncl : ", cl2) + fmt.Println("\ncl : ", cl3) + fmt.Println("\ncl : ", cl4) + fmt.Println("\nag : ", aga2) + fmt.Println("\nca : ", Ca1) + fmt.Println("\nca : ", Ca2) + }) +} + +func TestLink8() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + cl1 := shoset.NewShoset("cl", "cl") // cluster + cl1.InitPKI("localhost:8001") + + cl2 := shoset.NewShoset("cl", "cl") + cl2.Protocol("localhost:8002", "localhost:8001", "join") + + cl3 := shoset.NewShoset("cl", "cl") + cl3.Protocol("localhost:8003", "localhost:8002", "join") + + cl4 := shoset.NewShoset("cl", "cl") + cl4.Protocol("localhost:8004", "localhost:8001", "join") + + aga1 := shoset.NewShoset("aga", "a") // agregateur + aga1.Protocol("localhost:8111", "localhost:8001", "link") + + aga2 := shoset.NewShoset("aga", "a") // agregateur + aga2.Protocol("localhost:8112", "localhost:8002", "link") + + Ca2 := shoset.NewShoset("Ca", "c") //connecteur + Ca2.Protocol("localhost:8212", "localhost:8112", "link") + + // time.Sleep(time.Second * time.Duration(5)) + + // fmt.Println("\ncl : ", cl1) + // fmt.Println("\ncl : ", cl2) + // fmt.Println("\ncl : ", cl3) + // fmt.Println("\ncl : ", cl4) + // fmt.Println("\nag : ", aga1) + // fmt.Println("\nag : ", aga2) + // fmt.Println("\nca : ", Ca2) + + // time.Sleep(time.Second * time.Duration(5)) + + // cl1.Protocol("localhost:8001", "bye") + + // time.Sleep(time.Second * time.Duration(1)) + + // fmt.Println("\ncl : ", cl1) + // fmt.Println("\ncl : ", cl2) + // fmt.Println("\ncl : ", cl3) + // fmt.Println("\ncl : ", cl4) + // fmt.Println("\nag : ", aga1) + // fmt.Println("\nag : ", aga2) + // fmt.Println("\nca : ", Ca2) + + utilsForTest.LoopUntilDone(2*time.Second, ctx, func() { + // fmt.Println("\ncl : ", cl1) + // fmt.Println("\ncl : ", cl2) + // fmt.Println("\ncl : ", cl3) + // fmt.Println("\ncl : ", cl4) + // fmt.Println("\nag : ", aga1) + // fmt.Println("\nag : ", aga2) + // fmt.Println("\nca : ", Ca2) + // fmt.Println("ConnsByTypeArray('cl')", aga1.GetConnsByTypeArray("c")) + }) +} diff --git a/test/run_shoset.sh b/test/run_shoset.sh new file mode 100755 index 0000000..7af9f4a --- /dev/null +++ b/test/run_shoset.sh @@ -0,0 +1,9 @@ +# command : ./test/run_shoset.sh 4 + +### To use aliases instead : +# code -n ~/.bash_aliases +# source ~/.bash_aliases + +rm -rf ~/.shoset +go run -race test/test.go $1 +wait \ No newline at end of file diff --git a/test/test.go b/test/test.go index ce5ccd7..cf98a61 100644 --- a/test/test.go +++ b/test/test.go @@ -1,920 +1,518 @@ package main // tests run in the main package import ( + "context" "fmt" "os" + "path/filepath" + "sync" + "time" + // "os" // "log" - "time" - "github.com/ditrit/shoset" + "github.com/ditrit/shoset/msg" + "github.com/ditrit/shoset/test/example" + oldTest "github.com/ditrit/shoset/test/old_test" + utilsForTest "github.com/ditrit/shoset/test/utils_for_test" ) -func shosetClient(logicalName, ShosetType, address string) { - c := shoset.NewShoset(logicalName, ShosetType) - c.Protocol(address, "link") - - go func() { - for { - time.Sleep(time.Second * time.Duration(5)) - } - }() - /* - go func() { - command := msg.NewCommand("orchestrator", "deploy", "{\"appli\": \"toto\"}") - c.SendCommand(command) - event := msg.NewEvent("bus", "coucou", "ok") - c.SendEvent(event) - - events := msg.NewIterator(c.qEvents) - defer events.Close() - rec := c.WaitEvent(events, "bus", "started", 20) - if rec != nil { - fmt.Println(">Received Event: \n%#v\n", *rec) - } else { - fmt.Print("Timeout expired !") - } - events2 := msg.NewIterator(c.qEvents) - defer events.Close() - rec2 := c.WaitEvent(events2, "bus", "starting", 20) - if rec2 != nil { - fmt.Println(">Received Event 2: \n%#v\n", *rec2) - } else { - fmt.Print("Timeout expired 2 !") - } - }() - - */ - <-c.Done -} - -func shosetServer(logicalName, ShosetType, address string) { - s := shoset.NewShoset(logicalName, ShosetType) - err := s.Bind(address) - - if err != nil { - fmt.Println("Gandalf server socket can not be created") +func testPki(ctx context.Context, done context.CancelFunc) { + tt := []struct { + lname, stype, src, dst, ptype string + }{ + {lname: "cl", stype: "cl", src: "localhost:8001", dst: "", ptype: "pki"}, + {lname: "cl", stype: "cl", src: "localhost:8002", dst: "localhost:8001", ptype: "join"}, + {lname: "cl", stype: "cl", src: "localhost:8003", dst: "localhost:8002", ptype: "join"}, + {lname: "cl", stype: "cl", src: "localhost:8004", dst: "localhost:8001", ptype: "join"}, + {lname: "aga", stype: "a", src: "localhost:8111", dst: "localhost:8001", ptype: "link"}, + {lname: "aga", stype: "a", src: "localhost:8112", dst: "localhost:8002", ptype: "link"}, + {lname: "Ca", stype: "c", src: "localhost:8211", dst: "localhost:8111", ptype: "link"}, + {lname: "Ca", stype: "c", src: "localhost:8212", dst: "localhost:8112", ptype: "link"}, + {lname: "w", stype: "w", src: "localhost:8311", dst: "localhost:8211", ptype: "link"}, + {lname: "x", stype: "x", src: "localhost:8312", dst: "localhost:8212", ptype: "link"}, + {lname: "y", stype: "y", src: "localhost:8412", dst: "localhost:8312", ptype: "link"}, + {lname: "z", stype: "z", src: "localhost:8512", dst: "localhost:8412", ptype: "link"}, } - go func() { - for { - time.Sleep(time.Second * time.Duration(5)) + s := make([]*shoset.Shoset, len(tt)) + for i, t := range tt { + s[i] = shoset.NewShoset(t.lname, t.stype) + if t.ptype == "pki" { + s[i].InitPKI(t.src) + } else { + s[i].Protocol(t.src, t.dst, t.ptype) } - }() - /* - go func() { - time.Sleep(time.Second * time.Duration(5)) - event := msg.NewEvent("bus", "starting", "ok") - s.SendEvent(event) - time.Sleep(time.Millisecond * time.Duration(200)) - event = msg.NewEvent("bus", "started", "ok") - s.SendEvent(event) - command := msg.NewCommand("bus", "register", "{\"topic\": \"toto\"}") - s.SendCommand(command) - reply := msg.NewReply(command, "success", "OK") - s.SendReply(reply) - }() - */ - <-s.Done -} - -func shosetTest() { - done := make(chan bool) - - c1 := shoset.NewShoset("c", "c") - c1.Bind("localhost:8301") - - c2 := shoset.NewShoset("c", "c") - c2.Bind("localhost:8302") - - c3 := shoset.NewShoset("c", "c") - c3.Bind("localhost:8303") - - d1 := shoset.NewShoset("d", "a") - d1.Bind("localhost:8401") - - d2 := shoset.NewShoset("d", "a") - d2.Bind("localhost:8402") - - b1 := shoset.NewShoset("b", "c") - b1.Bind("localhost:8201") - b1.Protocol("localhost:8302", "link") - b1.Protocol("localhost:8301", "link") - b1.Protocol("localhost:8303", "link") - b1.Protocol("localhost:8401", "link") - b1.Protocol("localhost:8402", "link") - - a1 := shoset.NewShoset("a", "c") - a1.Bind("localhost:8101") - a1.Protocol("localhost:8201", "link") - - b2 := shoset.NewShoset("b", "c") - b2.Bind("localhost:8202") - b2.Protocol("localhost:8301", "link") - - b3 := shoset.NewShoset("b", "c") - b3.Bind("localhost:8203") - b3.Protocol("localhost:8303", "link") - - a2 := shoset.NewShoset("a", "c") - a2.Bind("localhost:8102") - a2.Protocol("localhost:8202", "link") - - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("a1 : ", a1.String()) - fmt.Println("a2 : ", a2.String()) - fmt.Println("b1 : ", b1.String()) - fmt.Println("b2 : ", b2.String()) - fmt.Println("b3 : ", b3.String()) - fmt.Println("c1 : ", c1.String()) - fmt.Println("c2 : ", c2.String()) - fmt.Println("c3 : ", c3.String()) - fmt.Println("d1 : ", d1.String()) - fmt.Println("d2 : ", d2.String()) - <-done -} - -func shosetTestEtoile() { - done := make(chan bool) - - cl1 := shoset.NewShoset("cl", "cl") - cl1.Bind("localhost:8001") - - cl2 := shoset.NewShoset("cl", "cl") - cl2.Bind("localhost:8002") - cl2.Protocol("localhost:8001", "join") - cl3 := shoset.NewShoset("cl", "cl") - cl3.Bind("localhost:8003") - cl3.Protocol("localhost:8002", "join") - - cl4 := shoset.NewShoset("cl", "cl") - cl4.Bind("localhost:8004") - cl4.Protocol("localhost:8001", "join") - - cl5 := shoset.NewShoset("cl", "cl") - cl5.Bind("localhost:8005") - cl5.Protocol("localhost:8001", "join") - - aga1 := shoset.NewShoset("aga", "a") - aga1.Bind("localhost:8111") - aga1.Protocol("localhost:8001", "link") - aga2 := shoset.NewShoset("aga", "a") - aga2.Bind("localhost:8112") - aga2.Protocol("localhost:8005", "link") - - agb1 := shoset.NewShoset("agb", "a") - agb1.Bind("localhost:8121") - agb1.Protocol("localhost:8002", "link") - agb2 := shoset.NewShoset("agb", "a") - agb2.Bind("localhost:8122") - agb2.Protocol("localhost:8003", "link") - - time.Sleep(time.Second * time.Duration(2)) - - Ca1 := shoset.NewShoset("Ca", "c") - Ca1.Bind("localhost:8211") - Ca1.Protocol("localhost:8111", "link") - Ca2 := shoset.NewShoset("Ca", "c") - Ca2.Bind("localhost:8212") - Ca2.Protocol("localhost:8111", "link") - Ca3 := shoset.NewShoset("Ca", "c") - Ca3.Bind("localhost:8213") - Ca3.Protocol("localhost:8111", "link") - - Cb1 := shoset.NewShoset("Cb", "c") - Cb1.Bind("localhost:8221") - Cb1.Protocol("localhost:8112", "link") - Cb2 := shoset.NewShoset("Cb", "c") - Cb2.Bind("localhost:8222") - Cb2.Protocol("localhost:8112", "link") - - Cc1 := shoset.NewShoset("Cc", "c") - Cc1.Bind("localhost:8231") - Cc1.Protocol("localhost:8111", "link") - Cc2 := shoset.NewShoset("Cc", "c") - Cc2.Bind("localhost:8232") - Cc2.Protocol("localhost:8111", "link") - - Cd1 := shoset.NewShoset("Cd", "c") - Cd1.Bind("localhost:8241") - Cd1.Protocol("localhost:8111", "link") - Cd2 := shoset.NewShoset("Cd", "c") - Cd2.Bind("localhost:8242") - Cd2.Protocol("localhost:8112", "link") - - Ce1 := shoset.NewShoset("Ce", "c") - Ce1.Bind("localhost:8251") - Ce1.Protocol("localhost:8122", "link") - Ce2 := shoset.NewShoset("Ce", "c") - Ce2.Bind("localhost:8252") - Ce2.Protocol("localhost:8122", "link") - - Cf1 := shoset.NewShoset("Cf", "c") - Cf1.Bind("localhost:8261") - Cf1.Protocol("localhost:8121", "link") - Cf2 := shoset.NewShoset("Cg", "c") - Cf2.Bind("localhost:8262") - Cf2.Protocol("localhost:8121", "link") - - Cg1 := shoset.NewShoset("Cg", "c") - Cg1.Bind("localhost:8271") - Cg1.Protocol("localhost:8121", "link") - Cg2 := shoset.NewShoset("Cg", "c") - Cg2.Bind("localhost:8272") - Cg2.Protocol("localhost:8122", "link") - - Ch1 := shoset.NewShoset("Ch", "c") - Ch1.Bind("localhost:8281") - Ch1.Protocol("localhost:8111", "link") - - time.Sleep(time.Second * time.Duration(2)) - fmt.Println("cl1 : ", cl2.String()) - fmt.Println("cl2 : ", cl2.String()) - fmt.Println("cl3 : ", cl3.String()) - fmt.Println("cl4 : ", cl4.String()) - fmt.Println("cl5 : ", cl5.String()) - - fmt.Println("aga1 : ", aga1.String()) - fmt.Println("aga2 : ", aga2.String()) - - fmt.Println("agb1 : ", agb1.String()) - fmt.Println("agb2 : ", agb2.String()) - - fmt.Println("Ca1 : ", Ca1.String()) - fmt.Println("Ca2 : ", Ca2.String()) - fmt.Println("Ca3 : ", Ca3.String()) - - fmt.Println("Cb1 : ", Cb1.String()) - fmt.Println("Cb2 : ", Cb2.String()) - - fmt.Println("Cc1 : ", Cc1.String()) - fmt.Println("Cc2 : ", Cc2.String()) - - fmt.Println("Cd1 : ", Cd1.String()) - fmt.Println("Cd2 : ", Cd2.String()) - - fmt.Println("Ce1 : ", Ce1.String()) - fmt.Println("Ce2 : ", Ce2.String()) - - fmt.Println("Cf1 : ", Cf1.String()) - fmt.Println("Cf2 : ", Cf2.String()) - - fmt.Println("Cg1 : ", Cg1.String()) - fmt.Println("Cg2 : ", Cg2.String()) - - fmt.Println("Ch1 : ", Ch1.String()) - - <-done -} - -func testQueue() { - done := make(chan bool) - /* // First let's make 2 sockets talk each other - C1 := shoset.NewShoset("C1", "c") - C1.Bind("localhost:8261") - C1.Protocol("localhost:8262","link") - - C2 := shoset.NewShoset("C2", "cl") - C2.Bind("localhost:8262") - C2.Protocol("localhost:8261","link") - - // Let's check for sockets connections - time.Sleep(time.Second * time.Duration(1)) - - fmt.Println("C1 : ", C1.String()) - fmt.Println("C2 : ", C2.String()) - - // Make C1 send a message to C2 - socket := C1.GetConnByAddr(C2.GetBindAddr()) - m := msg.NewCommand("test", "test", "content") - m.Timeout = 10000 - fmt.Println("Message Pushed: %+v\n", *m) - socket.SendMessage(m) - - // Let's dump C2 queue for cmd msg - time.Sleep(time.Second * time.Duration(1)) - cell := C2.FQueue("cmd").First() - fmt.Println("Cell in queue: %+v\n", *cell) - */<-done -} - -func simpleCluster() { - done := make(chan bool) - cl1 := shoset.NewShoset("cl", "cl") - cl1.Bind("localhost:8001") //we take the port 8001 for our first socket - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", cl1) } - <-done -} -func simpleAgregator() { - done := make(chan bool) - aga1 := shoset.NewShoset("aga", "a") // agregateur - aga1.Bind("localhost:8111") - aga1.Protocol("localhost:8001", "link") - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", aga1) - } - <-done -} - -func simpleConnector() { - done := make(chan bool) - Ca1 := shoset.NewShoset("Ca", "c") // agregateur - Ca1.Bind("localhost:8211") - Ca1.Protocol("localhost:8111", "link") - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", Ca1) - } - <-done -} + // time.Sleep(time.Second * time.Duration(2)) + // s[2].Protocol("localhost:8003", "localhost:8002", "bye") -func simplesimpleConnector() { - done := make(chan bool) - Ca1 := shoset.NewShoset("Ca", "c") // agregateur - Ca1.Bind("localhost:8211") - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", Ca1) - } - <-done -} - -func testJoin1() { - done := make(chan bool) - - cl1 := shoset.NewShoset("cl", "cl") - cl1.Bind("localhost:8001") - - cl2 := shoset.NewShoset("cl", "cl") // always "cl" "cl" for gandalf - cl2.Bind("localhost:8002") //we take the port 8002 for our first socket - cl2.Protocol("localhost:8001", "join") // we join it to our first socket - - cl3 := shoset.NewShoset("cl", "cl") - cl3.Bind("localhost:8003") - cl3.Protocol("localhost:8001", "join") - // cl3.Protocol("localhost:8002", "join") - - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", cl1) - fmt.Println("\ncl : ", cl2) - fmt.Println("\ncl : ", cl3) - } - - <-done -} - -func testJoin2() { - done := make(chan bool) - - cl2 := shoset.NewShoset("cl", "cl") // always "cl" "cl" for gandalf - cl2.Bind("localhost:8002") //we take the port 8002 for our first socket - cl2.Protocol("localhost:8001", "join") // we join it to our first socket - - cl3 := shoset.NewShoset("cl", "cl") - cl3.Bind("localhost:8003") - cl3.Protocol("localhost:8001", "join") - // cl3.Protocol("localhost:8002", "join") - - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", cl2) - fmt.Println("\ncl : ", cl3) - } - - <-done + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Println("in_callback") + for _, conn := range s { + fmt.Printf("%s: %v", conn.GetLogicalName(), conn) + } + done() + }) } -func testJoin3() { - done := make(chan bool) - +func testPkiServer(ctx context.Context, done context.CancelFunc) { cl1 := shoset.NewShoset("cl", "cl") // cluster - cl1.Bind("localhost:8001") + cl1.InitPKI("localhost:8001") - cl2 := shoset.NewShoset("cl", "cl") - cl2.Bind("localhost:8002") - cl2.Protocol("localhost:8001", "join") - - cl3 := shoset.NewShoset("cl", "cl") - cl3.Bind("localhost:8003") - cl3.Protocol("localhost:8002", "join") - - cl4 := shoset.NewShoset("cl", "cl") - cl4.Bind("localhost:8004") - cl4.Protocol("localhost:8001", "join") - - cl5 := shoset.NewShoset("cl", "cl") - cl5.Bind("localhost:8005") - cl5.Protocol("localhost:8004", "join") - - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", cl1) - fmt.Println("\ncl : ", cl2) - fmt.Println("\ncl : ", cl3) - fmt.Println("\ncl : ", cl4) - fmt.Println("\ncl : ", cl5) - } - - <-done + utilsForTest.LoopUntilDone(2*time.Second, ctx, func() { + // fmt.Println("\ncl : ", cl1) + done() + return + }) } -func testJoin4() { - done := make(chan bool) - - cl2 := shoset.NewShoset("cl", "cl") // always "cl" "cl" for gandalf - cl2.Bind("localhost:8002") //we take the port 8002 for our first socket - cl2.Protocol("localhost:8001", "join") // we join it to our first socket - - cl3 := shoset.NewShoset("cl", "cl") - cl3.Bind("localhost:8003") - // cl3.Protocol("localhost:8001", "join") - cl3.Protocol("localhost:8002", "join") - - cl4 := shoset.NewShoset("cl", "cl") - cl4.Bind("localhost:8004") - cl4.Protocol("localhost:8001", "join") // we join it to our first socket - - cl5 := shoset.NewShoset("cl", "cl") - cl5.Bind("localhost:8005") - cl5.Protocol("localhost:8004", "join") - - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", cl2) - fmt.Println("\ncl : ", cl3) - fmt.Println("\ncl : ", cl4) - fmt.Println("\ncl : ", cl5) - } - - <-done -} - -// func testJoin() { -// done := make(chan bool) - -// cl2 := shoset.NewShoset("cl", "cl") // always "cl" "cl" for gandalf -// fmt.Println("\ncl : ", cl2) -// cl2.Bind("localhost:8002") //we take the port 8002 for our first socket -// cl2.Protocol("localhost:8001", "join") // we join it to our first socket - -// cl3 := shoset.NewShoset("cl", "cl") -// cl3.Bind("localhost:8003") -// cl3.Protocol("localhost:8001", "join") -// cl3.Protocol("localhost:8002", "join") - -// cl4 := shoset.NewShoset("cl", "cl") -// cl4.Bind("localhost:8004") -// cl4.Protocol("localhost:8002", "join") // we join it to our first socket - -// for { -// time.Sleep(time.Second * time.Duration(2)) -// fmt.Println("\ncl : ", cl2) -// fmt.Println("\ncl : ", cl3) -// fmt.Println("\ncl : ", cl4) -// } - -// <-done -// } - -// func test_link() { -// done := make(chan bool) - -// cl1 := shoset.NewShoset("cl", "cl") // cluster -// cl1.Bind("localhost:8001") - -// cl2 := shoset.NewShoset("cl", "cl") -// cl2.Bind("localhost:8002") -// cl2.Protocol("localhost:8001","join") - -// cl3 := shoset.NewShoset("cl", "cl") -// cl3.Bind("localhost:8003") -// cl3.Protocol("localhost:8002","join") - -// aga1 := shoset.NewShoset("aga", "a") // agregateur -// aga1.Bind("localhost:8111") -// aga1.Protocol("localhost:8001","link") - -// aga2 := shoset.NewShoset("aga", "a") // agregateur -// aga2.Bind("localhost:8112") -// aga2.Protocol("localhost:8002","link") - -// Ca1 := shoset.NewShoset("Ca", "c") //connecteur -// Ca1.Bind("localhost:8211") -// Ca1.Protocol("localhost:8111","link") - -// time.Sleep(time.Second * time.Duration(3)) -// aga3 := shoset.NewShoset("aga", "a") // agregateur -// aga3.Bind("localhost:8113") -// aga3.Protocol("localhost:8002","link") - -// Ca2 := shoset.NewShoset("Ca", "c") //connecteur -// Ca2.Bind("localhost:8212") -// Ca2.Protocol("localhost:8113","link") - -// for { -// fmt.Println("\ncl : ", cl1) -// fmt.Println("\ncl : ", cl2) -// // fmt.Println("\n", cl2.ConnsByName) -// fmt.Println("\ncl : ", cl3) -// // fmt.Println("\nag : ", aga1) -// fmt.Println("\nag : ", aga2) -// fmt.Println("\nag : ", aga3) -// fmt.Println("\nca : ", Ca1) -// fmt.Println("\nca : ", Ca2) -// time.Sleep(time.Second * time.Duration(3)) -// } - -// <-done -// } - -func test_link1() { - done := make(chan bool) - - cl1 := shoset.NewShoset("cl", "cl") // cluster - cl1.Bind("localhost:8001") - +func testPkiClient(ctx context.Context, done context.CancelFunc) { cl2 := shoset.NewShoset("cl", "cl") - cl2.Bind("localhost:8002") - cl2.Protocol("localhost:8001", "join") - - cl3 := shoset.NewShoset("cl", "cl") - cl3.Bind("localhost:8003") - cl3.Protocol("localhost:8002", "join") - - cl4 := shoset.NewShoset("cl", "cl") - cl4.Bind("localhost:8004") - cl4.Protocol("localhost:8001", "join") - - aga1 := shoset.NewShoset("aga", "a") // agregateur - aga1.Bind("localhost:8111") - aga1.Protocol("localhost:8001", "link") + cl2.Protocol("localhost:8002", "localhost:8001", "join") - aga2 := shoset.NewShoset("aga", "a") // agregateur - aga2.Bind("localhost:8112") - aga2.Protocol("localhost:8001", "link") + cl3 := shoset.NewShoset("x", "x") + cl3.Protocol("localhost:8003", "localhost:8002", "link") - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", cl1) - fmt.Println("\ncl : ", cl2) - fmt.Println("\ncl : ", cl3) - fmt.Println("\ncl : ", cl4) - fmt.Println("\nag : ", aga1) - fmt.Println("\nag : ", aga2) - } + cl4 := shoset.NewShoset("y", "y") + cl4.Protocol("localhost:8004", "localhost:8003", "link") - <-done + utilsForTest.LoopUntilDone(2*time.Second, ctx, func() { + // fmt.Println("\ncl : ", cl2) + done() + }) } -func test_link2() { - done := make(chan bool) +func testPresentationENIB(ctx context.Context, done context.CancelFunc) { + cl1 := shoset.NewShoset("cl", "cl") + cl1.InitPKI("localhost:8001") cl2 := shoset.NewShoset("cl", "cl") - cl2.Bind("localhost:8002") - cl2.Protocol("localhost:8001", "join") + cl2.Protocol("localhost:8002", "localhost:8001", "join") cl3 := shoset.NewShoset("cl", "cl") - cl3.Bind("localhost:8003") - cl3.Protocol("localhost:8002", "join") + cl3.Protocol("localhost:8003", "localhost:8002", "join") cl4 := shoset.NewShoset("cl", "cl") - cl4.Bind("localhost:8004") - cl4.Protocol("localhost:8001", "join") - - aga1 := shoset.NewShoset("aga", "a") // agregateur - aga1.Bind("localhost:8111") - aga1.Protocol("localhost:8001", "link") - - aga2 := shoset.NewShoset("aga", "a") // agregateur - aga2.Bind("localhost:8112") - aga2.Protocol("localhost:8001", "link") - - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", cl2) - fmt.Println("\ncl : ", cl3) - fmt.Println("\ncl : ", cl4) - fmt.Println("\nag : ", aga1) - fmt.Println("\nag : ", aga2) - } - - <-done + cl4.Protocol("localhost:8004", "localhost:8001", "join") + + utilsForTest.LoopUntilDone(1*time.Second, ctx, func() { + fmt.Printf("%s: %v", cl1.GetLogicalName(), cl1) + fmt.Printf("%s: %v", cl2.GetLogicalName(), cl2) + fmt.Printf("%s: %v", cl3.GetLogicalName(), cl3) + fmt.Printf("%s: %v", cl4.GetLogicalName(), cl4) + done() + }) } -func test_link3() { - done := make(chan bool) +// #### Routing test - cl1 := shoset.NewShoset("cl", "cl") // cluster - cl1.Bind("localhost:8001") +func testRouteTable(ctx context.Context, done context.CancelFunc) { - cl2 := shoset.NewShoset("cl", "cl") - cl2.Bind("localhost:8002") - cl2.Protocol("localhost:8001", "join") + tt := utilsForTest.Circle // Choose the network topology for the test + s := []*shoset.Shoset{} + s = utilsForTest.CreateManyShosets(tt, s, false) + utilsForTest.WaitForManyShosets(s) - cl3 := shoset.NewShoset("cl", "cl") - cl3.Bind("localhost:8003") - cl3.Protocol("localhost:8002", "join") + time.Sleep(10 * time.Second) // Wait for Routing to happen - cl4 := shoset.NewShoset("cl", "cl") - cl4.Bind("localhost:8004") - cl4.Protocol("localhost:8001", "join") + utilsForTest.PrintManyShosets(s) - aga1 := shoset.NewShoset("aga", "a") // agregateur - aga1.Bind("localhost:8111") - aga1.Protocol("localhost:8001", "link") + tt = append(tt, &(utilsForTest.ShosetCreation{Lname: "F", ShosetType: "cl", LocalAddress: "localhost:8006", RemoteAddresses: []string{"localhost:8001", "localhost:8005"}, ProtocolType: "link", Launched: false})) - aga2 := shoset.NewShoset("aga", "a") // agregateur - aga2.Bind("localhost:8112") - aga2.Protocol("localhost:8002", "link") + s = utilsForTest.CreateManyShosets(tt, s, false) + utilsForTest.WaitForManyShosets(s) - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", cl1) - fmt.Println("\ncl : ", cl2) - fmt.Println("\ncl : ", cl3) - fmt.Println("\ncl : ", cl4) - fmt.Println("\nag : ", aga1) - fmt.Println("\nag : ", aga2) - } + time.Sleep(10 * time.Second) - <-done + utilsForTest.PrintManyShosets(s) } -func test_link4() { - done := make(chan bool) +func testForwardMessage(ctx context.Context, done context.CancelFunc) { + tt := utilsForTest.LinkedCircles + s := []*shoset.Shoset{} + s = utilsForTest.CreateManyShosets(tt, s, false) + utilsForTest.WaitForManyShosets(s) - cl2 := shoset.NewShoset("cl", "cl") - cl2.Bind("localhost:8002") - cl2.Protocol("localhost:8001", "join") + time.Sleep(5 * time.Second) // Allows routing to happen before displaying. - cl3 := shoset.NewShoset("cl", "cl") - cl3.Bind("localhost:8003") - cl3.Protocol("localhost:8002", "join") + utilsForTest.PrintManyShosets(s) - cl4 := shoset.NewShoset("cl", "cl") - cl4.Bind("localhost:8004") - cl4.Protocol("localhost:8001", "join") + var wg sync.WaitGroup - aga1 := shoset.NewShoset("aga", "a") // agregateur - aga1.Bind("localhost:8111") - aga1.Protocol("localhost:8001", "link") + destination := s[len(s)-1] - aga2 := shoset.NewShoset("aga", "a") // agregateur - aga2.Bind("localhost:8112") - aga2.Protocol("localhost:8002", "link") + // Receive Message + wg.Add(1) + go func() { + defer wg.Done() + event_rc := destination.Wait("simpleMessage", map[string]string{}, 30, nil) + fmt.Println("(Main) Message received : ", event_rc) + }() - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", cl2) - fmt.Println("\ncl : ", cl3) - fmt.Println("\ncl : ", cl4) - fmt.Println("\nag : ", aga1) - fmt.Println("\nag : ", aga2) - } + // Send Message + message := msg.NewSimpleMessage(destination.GetLogicalName(), "test_payload") + fmt.Println("Message sent : ", message) + s[0].Send(message) - <-done + wg.Wait() } -func test_link5() { - done := make(chan bool) - - cl1 := shoset.NewShoset("cl", "cl") // cluster - cl1.Bind("localhost:8001") - - cl2 := shoset.NewShoset("cl", "cl") - cl2.Bind("localhost:8002") - cl2.Protocol("localhost:8001", "join") - - cl3 := shoset.NewShoset("cl", "cl") - cl3.Bind("localhost:8003") - cl3.Protocol("localhost:8002", "join") - - cl4 := shoset.NewShoset("cl", "cl") - cl4.Bind("localhost:8004") - cl4.Protocol("localhost:8001", "join") - - aga1 := shoset.NewShoset("aga", "a") // agregateur - aga1.Bind("localhost:8111") - aga1.Protocol("localhost:8001", "link") - - aga2 := shoset.NewShoset("aga", "a") // agregateur - aga2.Bind("localhost:8112") - aga2.Protocol("localhost:8002", "link") - - Ca1 := shoset.NewShoset("Ca", "c") //connecteur - Ca1.Bind("localhost:8211") - Ca1.Protocol("localhost:8111", "link") - - Ca2 := shoset.NewShoset("Ca", "c") //connecteur - Ca2.Bind("localhost:8212") - Ca2.Protocol("localhost:8112", "link") - - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", cl1) - fmt.Println("\ncl : ", cl2) - fmt.Println("\ncl : ", cl3) - fmt.Println("\ncl : ", cl4) - fmt.Println("\nag : ", aga1) - fmt.Println("\nag : ", aga2) - fmt.Println("\nca : ", Ca1) - fmt.Println("\nca : ", Ca2) +func testForwardMessageMultiProcess(args []string) { + // args[0] is not the nama of the execuatable, it is the first argument after test number + // ./bin/shoset_build 5 D 0 0 rien 0 (args[0] is D) + fmt.Println("args : ", args) + + // To generate profiles and traces about only one shoset + // if args[0] != "D" { + + // //Disable tracer and profiles in the main. + + // // Tracer + // f, _ := os.Create("./profiler/trace_" + args[0] + ".out") + // defer f.Close() + // trace.Start(f) + // defer trace.Stop() + + // // CPU profiler + // var cpuprofile = flag.String("cpuprofile", "./profiler/cpu_"+args[0]+".prof", "write cpu profile to `file`") + + // flag.Parse() + // if *cpuprofile != "" { + // f, err := os.Create(*cpuprofile) + // if err != nil { + // log.Fatal("could not create CPU profile: ", err) + // } + // defer f.Close() // error handling omitted for example + // if err := pprof.StartCPUProfile(f); err != nil { + // log.Fatal("could not start CPU profile: ", err) + // } + // defer pprof.StopCPUProfile() + // } + // } + + topology := utilsForTest.CircleWrongOrder + + var cl *shoset.Shoset + if args[4] == "1" { + fmt.Println("#### Relaunch") + time.Sleep(3 * time.Second) + cl = utilsForTest.CreateShosetOnlyBindFromTopology(args[0], topology) + } else { + fmt.Println("#### Launch") + cl = utilsForTest.CreateShosetFromTopology(args[0], topology) } - <-done -} - -func test_link6() { - done := make(chan bool) - - cl2 := shoset.NewShoset("cl", "cl") - cl2.Bind("localhost:8002") - cl2.Protocol("localhost:8001", "join") + fmt.Println("Waiting for protocols to complete.") + cl.WaitForProtocols(10) + fmt.Println("Shoset : ", cl) - cl3 := shoset.NewShoset("cl", "cl") - cl3.Bind("localhost:8003") - cl3.Protocol("localhost:8002", "join") - - cl4 := shoset.NewShoset("cl", "cl") - cl4.Bind("localhost:8004") - cl4.Protocol("localhost:8001", "join") - - aga1 := shoset.NewShoset("aga", "a") // agregateur - aga1.Bind("localhost:8111") - aga1.Protocol("localhost:8001", "link") + // Receive Message + if args[1] == "1" { //args[1] receiver + fmt.Println("Receiver : ", cl.GetLogicalName()) + iterator := msg.NewIterator(cl.Queue["simpleMessage"]) + go func() { + for { + event_rc := cl.Wait("simpleMessage", map[string]string{}, 10, iterator) + fmt.Println("(main) Message received : ", event_rc) + } + }() - aga2 := shoset.NewShoset("aga", "a") // agregateur - aga2.Bind("localhost:8112") - aga2.Protocol("localhost:8002", "link") + } - Ca1 := shoset.NewShoset("Ca", "c") //connecteur - Ca1.Bind("localhost:8211") - Ca1.Protocol("localhost:8111", "link") + // Send Message + if args[2] == "1" { //args[2] sender + go func() { + for { + fmt.Println("Sender : ", cl.GetLogicalName()) + message := msg.NewSimpleMessage(args[3], "test_payload "+cl.GetLogicalName()) //args[3] destination + fmt.Println("Message sent : ", message) + cl.Send(message) + time.Sleep(1 * time.Second) + } + }() + } - Ca2 := shoset.NewShoset("Ca", "c") //connecteur - Ca2.Bind("localhost:8212") - Ca2.Protocol("localhost:8112", "link") + fmt.Println("#### Shoset ", cl.GetLogicalName(), "is ready.") for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", cl2) - fmt.Println("\ncl : ", cl3) - fmt.Println("\ncl : ", cl4) - fmt.Println("\nag : ", aga1) - fmt.Println("\nag : ", aga2) - fmt.Println("\nca : ", Ca1) - fmt.Println("\nca : ", Ca2) + time.Sleep(10 * time.Second) + + fmt.Println("Shoset ", cl.GetLogicalName(), " : ", cl) } - <-done + //select {} } -func test_link7() { - done := make(chan bool) +// Send an event every second forever : +func testEndConnection(ctx context.Context, done context.CancelFunc) { + tt := utilsForTest.Line3 // Choose the network topology for the test - cl1 := shoset.NewShoset("cl", "cl") // cluster - cl1.Bind("localhost:8001") + s := []*shoset.Shoset{} // Create the empty list of shosets - cl2 := shoset.NewShoset("cl", "cl") - cl2.Bind("localhost:8002") - cl2.Protocol("localhost:8001", "join") + s = utilsForTest.CreateManyShosets(tt, s, false) // Populate the list with the shosets as specified in the selected topology and estavlish connection among them - cl3 := shoset.NewShoset("cl", "cl") - cl3.Bind("localhost:8003") - cl3.Protocol("localhost:8002", "join") + utilsForTest.WaitForManyShosets(s) // Wait for every shosets in the list to be ready - cl4 := shoset.NewShoset("cl", "cl") - cl4.Bind("localhost:8004") - cl4.Protocol("localhost:8001", "join") - - aga2 := shoset.NewShoset("aga", "a") // agregateur - aga2.Bind("localhost:8112") - aga2.Protocol("localhost:8002", "link") - - Ca1 := shoset.NewShoset("Ca", "c") //connecteur - Ca1.Bind("localhost:8211") - Ca1.Protocol("localhost:8111", "link") + utilsForTest.PrintManyShosets(s) // Display the info of every shosets in the list - Ca2 := shoset.NewShoset("Ca", "c") //connecteur - Ca2.Bind("localhost:8212") - Ca2.Protocol("localhost:8112", "link") + destination := s[len(s)-1] + sender := s[0] - for { - time.Sleep(time.Second * time.Duration(1)) - fmt.Println("\ncl : ", cl1) - fmt.Println("\ncl : ", cl2) - fmt.Println("\ncl : ", cl3) - fmt.Println("\ncl : ", cl4) - fmt.Println("\nag : ", aga2) - fmt.Println("\nca : ", Ca1) - fmt.Println("\nca : ", Ca2) - } + //Sender : + go func() { + i := 0 + for { + time.Sleep(1 * time.Second) + message := msg.NewSimpleMessage(destination.GetLogicalName(), "test_payload"+fmt.Sprint(i)) + fmt.Println("Message sent : ", message) + sender.Send(message) + i++ + } + }() - <-done -} + //Receiver : + iterator := msg.NewIterator(destination.Queue["simpleMessage"]) + go func() { + for { + event_rc := destination.Wait("simpleMessage", map[string]string{}, 5, iterator) + fmt.Println("message received : ", event_rc) + if event_rc != nil { + shoset.Log("message received (Payload) : " + event_rc.GetPayload()) + } + } + }() -func test_link8() { - done := make(chan bool) + time.Sleep(0 * time.Second) - cl1 := shoset.NewShoset("cl", "cl") // cluster - cl1.Bind("localhost:8001") + fmt.Println("####", s[2].GetLogicalName(), " is ending connection to B") + s[2].EndProtocol("B", "127.0.0.1:8002") - cl2 := shoset.NewShoset("cl", "cl") - cl2.Bind("localhost:8002") - cl2.Protocol("localhost:8001", "join") + time.Sleep(5 * time.Second) - cl3 := shoset.NewShoset("cl", "cl") - cl3.Bind("localhost:8003") - cl3.Protocol("localhost:8002", "join") + utilsForTest.PrintManyShosets(s) - cl4 := shoset.NewShoset("cl", "cl") - cl4.Bind("localhost:8004") - cl4.Protocol("localhost:8001", "join") - - aga1 := shoset.NewShoset("aga", "a") // agregateur - aga1.Bind("localhost:8111") - aga1.Protocol("localhost:8001", "link") + time.Sleep(10 * time.Second) +} - aga2 := shoset.NewShoset("aga", "a") // agregateur - aga2.Bind("localhost:8112") - aga2.Protocol("localhost:8002", "link") +func TestFileTransferBetweenJoinNodes() { - Ca2 := shoset.NewShoset("Ca", "c") //connecteur - Ca2.Bind("localhost:8212") - Ca2.Protocol("localhost:8112", "link") + baseDir, err := os.UserHomeDir() + if err != nil { + fmt.Println(err) + } + baseDir1 := filepath.Join(baseDir, ".shoset", "A", "127-0-0-1_8001a") + cl1 := shoset.NewShoset("A", "TypeOfA") + cl1.InitLibrary(baseDir1) + cl1.InitPKI("localhost:8001") // Is the CA of the network + + baseDir2 := filepath.Join(baseDir, ".shoset", "A", "127-0-0-1_8002a") + cl2 := shoset.NewShoset("A", "TypeOfA") + cl2.InitLibrary(baseDir2) + cl2.Protocol("localhost:8002", "localhost:8001", "join") // we link it to our first socket + + cl1.WaitForProtocols(5) // Wait for cl1 to be ready + cl2.WaitForProtocols(5) + + //time.Sleep(5 * time.Second) + //fmt.Println(cl1) + //fmt.Println(cl2) + //fmt.Println(cl3) + //time.Sleep(2 * time.Second) + // setup the library for cl1 + pathTest, err := os.Getwd() + if err != nil { + fmt.Println(err) + } + if filepath.Base(pathTest) != "test" { + pathTest = filepath.Join(pathTest, "test", "testRep") + } else { + pathTest = filepath.Join(pathTest, "testRep") + } + fichier := "test10Mo.txt" + err = utilsForTest.GenerateFile(pathTest, "test10Mo.txt", 10*1024) + if err != nil { + fmt.Println(err) + } - time.Sleep(time.Second * time.Duration(5)) - - // fmt.Println("\ncl : ", cl1) - // fmt.Println("\ncl : ", cl2) - // fmt.Println("\ncl : ", cl3) - // fmt.Println("\ncl : ", cl4) - // fmt.Println("\nag : ", aga1) - // fmt.Println("\nag : ", aga2) - // fmt.Println("\nca : ", Ca2) + /* + file, err := os.Open(filepath.Join(myPath, fichier)) + if err != nil { + fmt.Println(err) + } + err = os.MkdirAll(baseDir1, 0755) + if err != nil { + fmt.Println(err) + } - // time.Sleep(time.Second * time.Duration(5)) + file2, err := os.Create(filepath.Join(baseDir1, fichier)) + if err != nil { + fmt.Println(err) + } + _, err = io.Copy(file2, file) + if err != nil { + fmt.Println(err) + } + file.Close() + file2.Close() + */ + fmt.Println("file copied") + path := filepath.Join(pathTest, fichier) + pathLibrary := filepath.Join(baseDir1, fichier) + err = cl1.FileCommands.Add(path, pathLibrary) + if err != nil { + fmt.Println("Error while adding file to library : ", err) + } + fmt.Println("file uploaded") - cl1.Protocol("localhost:8001", "bye") + time.Sleep(0 * time.Second) + baseDir3 := filepath.Join(baseDir, ".shoset", "A", "127-0-0-1_8003a") + cl3 := shoset.NewShoset("A", "TypeOfA") + cl3.InitLibrary(baseDir3) + cl3.Protocol("localhost:8003", "localhost:8002", "join") // we link it to our first socket + cl3.WaitForProtocols(5) - // time.Sleep(time.Second * time.Duration(1)) + time.Sleep(50 * time.Second) +} - // fmt.Println("\ncl : ", cl1) - // fmt.Println("\ncl : ", cl2) - // fmt.Println("\ncl : ", cl3) - // fmt.Println("\ncl : ", cl4) - // fmt.Println("\nag : ", aga1) - // fmt.Println("\nag : ", aga2) - // fmt.Println("\nca : ", Ca2) +func TestTCPInfo() { - for { - time.Sleep(time.Second * time.Duration(2)) - fmt.Println("\ncl : ", cl1) - fmt.Println("\ncl : ", cl2) - fmt.Println("\ncl : ", cl3) - fmt.Println("\ncl : ", cl4) - fmt.Println("\nag : ", aga1) - fmt.Println("\nag : ", aga2) - fmt.Println("\nca : ", Ca2) - // fmt.Println("ConnsByTypeArray('cl')", aga1.GetConnsByTypeArray("c")) + baseDir, err := os.UserHomeDir() + if err != nil { + fmt.Println(err) } + baseDir1 := filepath.Join(baseDir, ".shoset", "A", "127-0-0-1_8001a") + cl1 := shoset.NewShoset("A", "TypeOfA") + cl1.InitLibrary(baseDir1) + cl1.InitPKI("localhost:8001") // Is the CA of the network + + baseDir2 := filepath.Join(baseDir, ".shoset", "A", "127-0-0-1_8002a") + cl2 := shoset.NewShoset("A", "TypeOfA") + cl2.InitLibrary(baseDir2) + cl2.Protocol("localhost:8002", "localhost:8001", "join") // we link it to our first socket + + cl1.WaitForProtocols(5) // Wait for cl1 to be ready + cl2.WaitForProtocols(5) + + cl1.ConnsByLname.Iterate(func(key string, key2 string, value interface{}) { + conn := value.(*shoset.ShosetConn) + fmt.Println("conn", conn.GetRemoteAddress()) + tcp, err := conn.GetTCPInfo() + if err != nil { + fmt.Println(err) + } + fmt.Println("tcpinfo :") + fmt.Println("fenetre de congestion", tcp.Snd_cwnd) + fmt.Println("RTT", tcp.Rtt) + fmt.Println("receive RTT", tcp.Rcv_rtt) + fmt.Println("fenetre de reception estimée", tcp.Rcv_space) - <-done + }) + + time.Sleep(50 * time.Second) } func main() { + // Clear the content of the profiler folder + // os.RemoveAll("./profiler/") + // os.MkdirAll("./profiler/", 0777) + + // tracer + // f, _ := os.Create("./profiler/trace.out") + // defer f.Close() + // trace.Start(f) + // defer trace.Stop() + + // CPU profiler + // var cpuprofile = flag.String("cpuprofile", "./profiler/cpu.prof", "write cpu profile to `file`") + // flag.Parse() + // if *cpuprofile != "" { + // f, err := os.Create(*cpuprofile) + // if err != nil { + // log.Fatal("could not create CPU profile: ", err) + // } + // defer f.Close() // error handling omitted for example + // if err := pprof.StartCPUProfile(f); err != nil { + // log.Fatal("could not start CPU profile: ", err) + // } + // defer pprof.StopCPUProfile() + // } + + shoset.InitPrettyLogger(true) + shoset.SetLogLevel(shoset.TRACE) + + ctx, done := context.WithTimeout(context.Background(), 1*time.Minute) + //terminal + // Choose the test to run, only decomment one for each case. arg := os.Args[1] - fmt.Println(arg) if arg == "1" { - // testJoin1() - // testJoin2() - // testJoin3() - // testJoin4() - // test_link1() - // test_link2() - // test_link3() - // test_link4() - // test_link5() - // test_link6() - // test_link7() - test_link8() + shoset.Log("testPkiServer") + // testPkiServer(ctx, done) + oldTest.TestJoin1() + // oldTest.TestJoin2() + // oldTest.TestJoin3() + // oldTest.TestJoin4() + // oldTest.TestLink1() + // oldTest.TestLink2() + // oldTest.TestLink3() + // oldTest.TestLink4() + // oldTest.TestLink5() + // oldTest.TestLink6() + // oldTest.TestLink7() + // oldTest.TestLink8() + // testPki() } else if arg == "2" { - // simpleCluster() - // simpleAgregator() - simpleConnector() + shoset.Log("testPkiClient") + // testPkiClient(ctx, done) + // oldTest.SimpleCluster() + // oldTest.SimpleAgregator() + // oldTest.SimpleConnector() } else if arg == "3" { - simplesimpleConnector() - } else { - shosetTestEtoile() + shoset.Log("simplesimpleConnector") + //oldTest.SimplesimpleConnector() + } else if arg == "4" { + // testPki(ctx, done) + // testPresentationENIB(ctx, done) + // oldTest.TestJoin3(ctx, done) + + // testRouteTable(ctx, done) + testForwardMessage(ctx, done) + // testEndConnection(ctx, done) + } else if arg == "5" { + //testForwardMessageMultiProcess((os.Args)[2:]) + example.Test3Shosets() + } else if arg == "6" { + // example.SimpleExample() + //example.TestEventContinuousSend() + //example.Test3Shosets() + // example.TestSimpleForwarding() + //example.TestForwardingTopology() + example.TestMutltipleShosets() + //example.Test3ShosetsCommand() + //example.Test2ShosetsCommand() + } else if arg == "7" { + example.Test3ShosetsCommand() + } else if arg == "8" { + TestFileTransferBetweenJoinNodes() + //TestTCPInfo() } + // Memory profiler + // var memprofile = flag.String("memprofile", "./profiler/mem.prof", "write memory profile to `file`") + + // if *memprofile != "" { + // f, err := os.Create(*memprofile) + // if err != nil { + // log.Fatal("could not create memory profile: ", err) + // } + // defer f.Close() // error handling omitted for example + // runtime.GC() // get up-to-date statistics + // if err := pprof.WriteHeapProfile(f); err != nil { + // log.Fatal("could not write memory profile: ", err) + // } + // } } // linkOk diff --git a/test/traffic.yaml b/test/traffic.yaml new file mode 100644 index 0000000..98447b1 --- /dev/null +++ b/test/traffic.yaml @@ -0,0 +1,72 @@ +# This file is to use with TrafficTroll to limit the bandwidth of an application. see the guide.md and the fileSynchronisation.md for more informations. + + + +# The rate limits for the specified interface. Specifying these values is useful for two +# things: 1) you want to limit the used bandwidth for the entire interface or 2) you +# want to make use of traffic prioritization. + +# If you want to 1) limit the used bandwidth for the entire interface, simply specify +# values below your actual speed: the traffic will be shaped in such a way, that it does +# not exceed the specified numbers. + +# If you want to 2) make use of the traffic prioritization feature, these values must be +# as close as possible to your real speed: if they are too low, traffic prioritization +# will work, but you are losing part of your bandwidth; if they are too high, traffic +# prioritization won't work as well as it could. I recommend you use some internet speed +# test you can find online to get an approximation for the correct values. + +# If you don't want to do 1) or 2), you can omit these values. Bandwidth limiting per +# application will still work, just traffic prioritization won't work as well or +# entirely. +#download: 5mbps +#upload: 1mbps + +# Guaranteed download and upload rates for all global traffic that is not shaped as part +# of a matched process by TrafficToll. The idea here is to leave enough "guaranteed" +# bandwidth to all applications not defined in "processes", so that they are not starved +# to a bandwidth, by processes with higher priority, that would cause the other IP to +# drop the connection. These are the default values, if omitted. Keep in mind that this +# doesn't reserve the bandwidth -- if this traffic is not made use of, it's available +# to processes with higher priority. +#download-minimum: 100kbps +#upload-minimum: 10kbps + +# The global download and upload priority. This will be the priority for traffic that is +# not created by any of the processes. By default it will always be the lowest priority +# if any of the processes specify a download or upload priority explicitly, otherwise +# all traffic will have the same priority. +#download-priority: 0 +#upload-priority: 0 + + +# A list of processes you want to match and their respective settings +processes: + # You can name the process what you want, it is only used to identify it on the CLI + # output + Test1: + # Adjust the traffic priority to 0 (highest possible, higher integers mean _lower_ + # priority) to prevent lag and high pings. + #download-priority: 0 + #upload-priority: 0 + + # Download and upload rate limits can be entirely omitted if you don't want to apply + # any, in this case traffic will only be prioritized like specified. + download: 1024kbps + upload: 1024kbps + + # The match section. A process is selected when all predicates in the match section + # match. Every attribute psutil.Process + # (https://psutil.readthedocs.io/en/latest/index.html#psutil.Process) provides on + # Linux can be matched on, using regular expressions. Integer attributes will be + # treated as strings and list attributes will be joined using shlex.join() before + # matching. If you want to, you can also specify a regular expression with an OR + # operator and match many processes which will all share the specified bandwidth + # limit or traffic priority. + # If you do not see a line starting with "Shaping traffic for..." with your process + # name in it, while it is clearly causing traffic, your match section is failing. + # Please make sure it works correctly. + match: + - name: test + + diff --git a/test/utils_for_test/topology.go b/test/utils_for_test/topology.go new file mode 100644 index 0000000..212cdcd --- /dev/null +++ b/test/utils_for_test/topology.go @@ -0,0 +1,104 @@ +package utilsForTest + +// List of instruction for launching a shoset. +type ShosetCreation struct { + Lname string + ShosetType string + LocalAddress string + RemoteAddresses []string + ProtocolType string // type of connection (pki (master), link, join, ...) + Launched bool // true when the shoset has been launched + //Used to extend the list after the first launch by calling again CreateManyShosets or CreateShosetFromTopology. +} + +// #### List of topologies + +var Line2 = []*ShosetCreation{ + {Lname: "A", ShosetType: "cl", LocalAddress: "localhost:8001", RemoteAddresses: []string{""}, ProtocolType: "pki", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: "localhost:8002", RemoteAddresses: []string{"localhost:8001"}, ProtocolType: "link", Launched: false}, +} + +var Line2With3Shosets = []*ShosetCreation{ + {Lname: "A", ShosetType: "cl", LocalAddress: "localhost:8001", RemoteAddresses: []string{""}, ProtocolType: "pki", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: "localhost:8002", RemoteAddresses: []string{"localhost:8001"}, ProtocolType: "link", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: "localhost:8003", RemoteAddresses: []string{"localhost:8001"}, ProtocolType: "link", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: "localhost:8004", RemoteAddresses: []string{"localhost:8002"}, ProtocolType: "join", Launched: false}, + {Lname: "A", ShosetType: "cl", LocalAddress: "localhost:8005", RemoteAddresses: []string{"localhost:8001"}, ProtocolType: "join", Launched: false}, +} + +var Line2With2Shosets = []*ShosetCreation{ + {Lname: "A", ShosetType: "cl", LocalAddress: "localhost:8001", RemoteAddresses: []string{""}, ProtocolType: "pki", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: "localhost:8002", RemoteAddresses: []string{"localhost:8001"}, ProtocolType: "link", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: "localhost:8003", RemoteAddresses: []string{"localhost:8002"}, ProtocolType: "join", Launched: false}, +} + +var Line3 = []*ShosetCreation{ + {Lname: "A", ShosetType: "cl", LocalAddress: "localhost:8001", RemoteAddresses: []string{""}, ProtocolType: "pki", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: "localhost:8002", RemoteAddresses: []string{"localhost:8001"}, ProtocolType: "link", Launched: false}, + {Lname: "C", ShosetType: "cl", LocalAddress: "localhost:8003", RemoteAddresses: []string{"localhost:8002"}, ProtocolType: "link", Launched: false}, +} + +var Line3WithMultipleShosets = []*ShosetCreation{ + {Lname: "A", ShosetType: "cl", LocalAddress: "localhost:8001", RemoteAddresses: []string{""}, ProtocolType: "pki", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: "localhost:8002", RemoteAddresses: []string{"localhost:8001"}, ProtocolType: "link", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: "localhost:8003", RemoteAddresses: []string{"localhost:8001"}, ProtocolType: "link", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: "localhost:8004", RemoteAddresses: []string{"localhost:8001"}, ProtocolType: "link", Launched: false}, + {Lname: "C", ShosetType: "cl", LocalAddress: "localhost:8005", RemoteAddresses: []string{"localhost:8002"}, ProtocolType: "link", Launched: false}, + {Lname: "C", ShosetType: "cl", LocalAddress: "localhost:8006", RemoteAddresses: []string{"localhost:8002"}, ProtocolType: "link", Launched: false}, +} + +var StraightLine = []*ShosetCreation{ + {Lname: "A", ShosetType: "cl", LocalAddress: "localhost:8001", RemoteAddresses: []string{""}, ProtocolType: "pki", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: "localhost:8002", RemoteAddresses: []string{"localhost:8001"}, ProtocolType: "link", Launched: false}, + {Lname: "C", ShosetType: "cl", LocalAddress: "localhost:8003", RemoteAddresses: []string{"localhost:8002"}, ProtocolType: "link", Launched: false}, + {Lname: "D", ShosetType: "cl", LocalAddress: "localhost:8004", RemoteAddresses: []string{"localhost:8003"}, ProtocolType: "link", Launched: false}, + {Lname: "E", ShosetType: "cl", LocalAddress: "localhost:8005", RemoteAddresses: []string{"localhost:8004"}, ProtocolType: "link", Launched: false}, +} + +var StraightLineWrongOrder = []*ShosetCreation{ + {Lname: "A", ShosetType: "cl", LocalAddress: "localhost:8001", RemoteAddresses: []string{""}, ProtocolType: "pki", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: "localhost:8002", RemoteAddresses: []string{"localhost:8001"}, ProtocolType: "link", Launched: false}, + {Lname: "C", ShosetType: "cl", LocalAddress: "localhost:8003", RemoteAddresses: []string{"localhost:8002", "localhost:8004"}, ProtocolType: "link", Launched: false}, + {Lname: "D", ShosetType: "cl", LocalAddress: "localhost:8004", RemoteAddresses: []string{"localhost:8001"}, ProtocolType: "link", Launched: false}, + {Lname: "E", ShosetType: "cl", LocalAddress: "localhost:8005", RemoteAddresses: []string{"localhost:8004"}, ProtocolType: "link", Launched: false}, +} + +var IPbyLname = map[string]string{ + "A": "localhost:8001", + "B": "localhost:8002", + "C": "localhost:8003", + "D": "localhost:8004", + "E": "localhost:8005", + "F": "localhost:8006", + "G": "localhost:8007", + "H": "localhost:8008", + "I": "localhost:8009", +} + +var LinkedCircles = []*ShosetCreation{ + {Lname: "A", ShosetType: "cl", LocalAddress: IPbyLname["A"], RemoteAddresses: []string{IPbyLname["D"]}, ProtocolType: "pki", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: IPbyLname["B"], RemoteAddresses: []string{IPbyLname["A"]}, ProtocolType: "link", Launched: false}, + {Lname: "C", ShosetType: "cl", LocalAddress: IPbyLname["C"], RemoteAddresses: []string{IPbyLname["A"]}, ProtocolType: "link", Launched: false}, + {Lname: "D", ShosetType: "cl", LocalAddress: IPbyLname["D"], RemoteAddresses: []string{IPbyLname["B"], IPbyLname["C"]}, ProtocolType: "link", Launched: false}, + {Lname: "E", ShosetType: "cl", LocalAddress: IPbyLname["E"], RemoteAddresses: []string{IPbyLname["D"], IPbyLname["F"]}, ProtocolType: "link", Launched: false}, + {Lname: "F", ShosetType: "cl", LocalAddress: IPbyLname["F"], RemoteAddresses: []string{IPbyLname["E"]}, ProtocolType: "link", Launched: false}, + {Lname: "G", ShosetType: "cl", LocalAddress: IPbyLname["G"], RemoteAddresses: []string{IPbyLname["F"]}, ProtocolType: "link", Launched: false}, + {Lname: "H", ShosetType: "cl", LocalAddress: IPbyLname["H"], RemoteAddresses: []string{IPbyLname["F"]}, ProtocolType: "link", Launched: false}, + {Lname: "I", ShosetType: "cl", LocalAddress: IPbyLname["I"], RemoteAddresses: []string{IPbyLname["G"], IPbyLname["H"]}, ProtocolType: "link", Launched: false}, +} + +var Circle = []*ShosetCreation{ + {Lname: "A", ShosetType: "cl", LocalAddress: IPbyLname["A"], RemoteAddresses: []string{}, ProtocolType: "pki", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: IPbyLname["B"], RemoteAddresses: []string{IPbyLname["A"]}, ProtocolType: "link", Launched: false}, + {Lname: "C", ShosetType: "cl", LocalAddress: IPbyLname["C"], RemoteAddresses: []string{IPbyLname["A"]}, ProtocolType: "link", Launched: false}, + {Lname: "D", ShosetType: "cl", LocalAddress: IPbyLname["D"], RemoteAddresses: []string{IPbyLname["C"]}, ProtocolType: "link", Launched: false}, + {Lname: "E", ShosetType: "cl", LocalAddress: IPbyLname["E"], RemoteAddresses: []string{IPbyLname["D"], IPbyLname["B"]}, ProtocolType: "link", Launched: false}, +} + +var CircleWrongOrder = []*ShosetCreation{ + {Lname: "A", ShosetType: "cl", LocalAddress: IPbyLname["A"], RemoteAddresses: []string{}, ProtocolType: "pki", Launched: false}, + {Lname: "B", ShosetType: "cl", LocalAddress: IPbyLname["B"], RemoteAddresses: []string{IPbyLname["A"], IPbyLname["E"]}, ProtocolType: "link", Launched: false}, + {Lname: "C", ShosetType: "cl", LocalAddress: IPbyLname["C"], RemoteAddresses: []string{IPbyLname["A"]}, ProtocolType: "link", Launched: false}, + {Lname: "D", ShosetType: "cl", LocalAddress: IPbyLname["D"], RemoteAddresses: []string{IPbyLname["C"]}, ProtocolType: "link", Launched: false}, + {Lname: "E", ShosetType: "cl", LocalAddress: IPbyLname["E"], RemoteAddresses: []string{IPbyLname["D"]}, ProtocolType: "link", Launched: false}, +} diff --git a/test/utils_for_test/utils.go b/test/utils_for_test/utils.go new file mode 100644 index 0000000..1e15e60 --- /dev/null +++ b/test/utils_for_test/utils.go @@ -0,0 +1,118 @@ +package utilsForTest + +import ( + "context" + "fmt" + "os" + "path/filepath" + "time" + + "github.com/ditrit/shoset" + "github.com/ditrit/shoset/msg" +) + +func LoopUntilDone(tick time.Duration, ctx context.Context, callback func()) { + ticker := time.NewTicker(tick) + for { + select { + case <-ctx.Done(): + fmt.Println("leaving...") + return + case t := <-ticker.C: + fmt.Println("Tick at", t) + callback() + } + } +} + +// Launche shoset using the list of instructions provided and add them to the list (the modified list id returned) +func CreateManyShosets(tt []*ShosetCreation, s []*shoset.Shoset, wait bool) []*shoset.Shoset { + for i, t := range tt { + if !t.Launched { + s = append(s, shoset.NewShoset(t.Lname, t.ShosetType)) + if t.ProtocolType == "pki" { + s[i].InitPKI(t.LocalAddress) + } else { + for _, a := range t.RemoteAddresses { + s[i].Protocol(t.LocalAddress, a, t.ProtocolType) // Launch as goroutine is to fast for WaitForProtocol. + } + } + t.Launched = true + } + } + if wait { + time.Sleep(1 * time.Second) + } + + return s +} + +// Create a single Shoset from the topology (for multiProcesses tests) +func CreateShosetFromTopology(Lname string, tt []*ShosetCreation) *shoset.Shoset { + for _, t := range tt { + if !t.Launched && t.Lname == Lname { + s := shoset.NewShoset(t.Lname, t.ShosetType) + if t.ProtocolType == "pki" { + s.InitPKI(t.LocalAddress) + } else { + for _, a := range t.RemoteAddresses { + s.Protocol(t.LocalAddress, a, t.ProtocolType) + } + } + t.Launched = true + return s + } + } + return nil +} + +// Same as CreateShosetFromTopology but only launches an empty protocol to relaunch the connections saved in the config file +func CreateShosetOnlyBindFromTopology(Lname string, tt []*ShosetCreation) *shoset.Shoset { + for _, t := range tt { + if !t.Launched && t.Lname == Lname { + s := shoset.NewShoset(t.Lname, t.ShosetType) + s.Protocol(t.LocalAddress, "", "") + t.Launched = true + return s + } + } + return nil +} + +func PrintManyShosets(s []*shoset.Shoset) { + for i, t := range s { + fmt.Println("\nShoset ", i, ": ", t) + } +} + +// Waits for every shosets in the list to be ready +func WaitForManyShosets(s []*shoset.Shoset) { + for _, t := range s { + t.WaitForProtocols(10) + } +} + +// Forces reroute of every shosets in the list. +func RouteManyShosets(s []*shoset.Shoset, wait bool) { + for _, t := range s { + routing := msg.NewRoutingEvent(t.GetLogicalName(), true, 0, "") + t.Send(routing) + } + if wait { + time.Sleep(1 * time.Second) + } +} + +// generate a file IF IT DOESNT EXIST in the path with the name and the size in KB +func GenerateFile(path string, name string, size int) error { + _, err := os.Stat(filepath.Join(path, name)) + if err == nil { + return nil + } + buffer := make([]byte, size*1024) + for i := 0; i < size*1024; i++ { + buffer[i] = 'a' + } + err = os.WriteFile(filepath.Join(path, name), buffer, 0644) + return err +} diff --git a/uml/call_graph/msg.svg b/uml/call_graph/msg.svg new file mode 100644 index 0000000..c8bd4a0 --- /dev/null +++ b/uml/call_graph/msg.svg @@ -0,0 +1,2941 @@ + + + + + + +gocallvis + +github.com/ditrit/shoset/test + +cluster_focus + +msg + + +cluster_*github.com/ditrit/shoset/msg.Cell + + +(*Cell) + + + + +cluster_*github.com/ditrit/shoset/msg.Iterator + + +(*Iterator) + + + + +cluster_*github.com/ditrit/shoset/msg.MessageBase + + +(*MessageBase) + + + + +cluster_*github.com/ditrit/shoset/msg.PkiEvent + + +(*PkiEvent) + + + + +cluster_*github.com/ditrit/shoset/msg.Queue + + +(*Queue) + + + + +cluster_*github.com/ditrit/shoset/msg.Reader + + +(*Reader) + + + + +cluster_*github.com/ditrit/shoset/msg.RoutingEvent + + +(*RoutingEvent) + + + + +cluster_*github.com/ditrit/shoset/msg.Writer + + +(*Writer) + + + + +cluster_github.com/ditrit/shoset + + +shoset + + + + +cluster_*github.com/ditrit/shoset.CommandHandler + + +(*CommandHandler) + + + + +cluster_*github.com/ditrit/shoset.ConfigByeHandler + + +(*ConfigByeHandler) + + + + +cluster_*github.com/ditrit/shoset.ConfigHandler + + +(*ConfigHandler) + + + + +cluster_*github.com/ditrit/shoset.ConfigJoinHandler + + +(*ConfigJoinHandler) + + + + +cluster_*github.com/ditrit/shoset.ConfigLinkHandler + + +(*ConfigLinkHandler) + + + + +cluster_*github.com/ditrit/shoset.EventHandler + + +(*EventHandler) + + + + +cluster_*github.com/ditrit/shoset.ForwardAcknownledgeHandler + + +(*ForwardAcknownledgeHandler) + + + + +cluster_*github.com/ditrit/shoset.PkiEventHandler + + +(*PkiEventHandler) + + + + +cluster_*github.com/ditrit/shoset.RoutingEventHandler + + +(*RoutingEventHandler) + + + + +cluster_*github.com/ditrit/shoset.Shoset + + +(*Shoset) + + + + +cluster_*github.com/ditrit/shoset.ShosetConn + + +(*ShosetConn) + + + + +cluster_*github.com/ditrit/shoset.SimpleMessageHandler + + +(*SimpleMessageHandler) + + + + +cluster_github.com/ditrit/shoset/msg.Command + + +(Command) + + + + +cluster_github.com/ditrit/shoset/msg.Config + + +(Config) + + + + +cluster_github.com/ditrit/shoset/msg.ConfigProtocol + + +(ConfigProtocol) + + + + +cluster_github.com/ditrit/shoset/msg.Event + + +(Event) + + + + +cluster_github.com/ditrit/shoset/msg.ForwardAck + + +(ForwardAck) + + + + +cluster_github.com/ditrit/shoset/msg.MessageBase + + +(MessageBase) + + + + +cluster_github.com/ditrit/shoset/msg.PkiEvent + + +(PkiEvent) + + + + +cluster_github.com/ditrit/shoset/msg.RoutingEvent + + +(RoutingEvent) + + + + +cluster_github.com/ditrit/shoset/msg.SimpleMessage + + +(SimpleMessage) + + + + +cluster_github.com/ditrit/shoset/test + + +main + + + + +cluster_github.com/ditrit/shoset/test/example + + +example + + + + + +github.com/ditrit/shoset/msg.NewRoutingEvent + + +NewRoutingEvent + + + + + +(*github.com/ditrit/shoset/msg.MessageBase).InitMessageBase + + +InitMessageBase + + + + + +github.com/ditrit/shoset/msg.NewRoutingEvent->(*github.com/ditrit/shoset/msg.MessageBase).InitMessageBase + + + + + + + + +(*github.com/ditrit/shoset/msg.MessageBase).SetUUID + + +SetUUID + + + + + +github.com/ditrit/shoset/msg.NewRoutingEvent->(*github.com/ditrit/shoset/msg.MessageBase).SetUUID + + + + + + + + +(*github.com/ditrit/shoset/msg.RoutingEvent).SetRerouteTimestamp + + +SetRerouteTimestamp + + + + + +github.com/ditrit/shoset/msg.NewRoutingEvent->(*github.com/ditrit/shoset/msg.RoutingEvent).SetRerouteTimestamp + + + + + + + + +github.com/ditrit/shoset/msg.NewIterator + + +NewIterator + + + + + +(*github.com/ditrit/shoset/msg.Iterator).Init + + +Init + + + + + +github.com/ditrit/shoset/msg.NewIterator->(*github.com/ditrit/shoset/msg.Iterator).Init + + + + + + + + +github.com/ditrit/shoset/msg.NewPkiEventReturn + + +NewPkiEventReturn + + + + + +github.com/ditrit/shoset/msg.NewPkiEventReturn->(*github.com/ditrit/shoset/msg.MessageBase).InitMessageBase + + + + + + + + +github.com/ditrit/shoset/msg.NewEvent + + +NewEvent + + + + + +github.com/ditrit/shoset/msg.NewEvent->(*github.com/ditrit/shoset/msg.MessageBase).InitMessageBase + + + + + + + + +github.com/ditrit/shoset/msg.NewEventClassic + + +NewEventClassic + + + + + +github.com/ditrit/shoset/msg.NewEventClassic->github.com/ditrit/shoset/msg.NewEvent + + + + + + + + +github.com/ditrit/shoset/msg.NewConfigProtocol + + +NewConfigProtocol + + + + + +github.com/ditrit/shoset/msg.NewConfigProtocol->(*github.com/ditrit/shoset/msg.MessageBase).InitMessageBase + + + + + + + + +github.com/ditrit/shoset/msg.NewPkiEventInit + + +NewPkiEventInit + + + + + +github.com/ditrit/shoset/msg.NewPkiEventInit->(*github.com/ditrit/shoset/msg.MessageBase).InitMessageBase + + + + + + + + +github.com/ditrit/shoset/msg.NewForwardAck + + +NewForwardAck + + + + + +github.com/ditrit/shoset/msg.NewForwardAck->(*github.com/ditrit/shoset/msg.MessageBase).InitMessageBase + + + + + + + + +github.com/ditrit/shoset/msg.NewConfigBrothersProtocol + + +NewConfigBrothersProtocol + + + + + +github.com/ditrit/shoset/msg.NewConfigBrothersProtocol->(*github.com/ditrit/shoset/msg.MessageBase).InitMessageBase + + + + + + + + +github.com/ditrit/shoset/msg.NewQueue + + +NewQueue + + + + + +(*github.com/ditrit/shoset/msg.Queue).Init + + +Init + + + + + +github.com/ditrit/shoset/msg.NewQueue->(*github.com/ditrit/shoset/msg.Queue).Init + + + + + + + + +github.com/ditrit/shoset/msg.NewSimpleMessage + + +NewSimpleMessage + + + + + +github.com/ditrit/shoset/msg.NewSimpleMessage->(*github.com/ditrit/shoset/msg.MessageBase).InitMessageBase + + + + + + + + +(*github.com/ditrit/shoset/msg.MessageBase).SetDestinationLname + + +SetDestinationLname + + + + + +github.com/ditrit/shoset/msg.NewSimpleMessage->(*github.com/ditrit/shoset/msg.MessageBase).SetDestinationLname + + + + + + + + +(*github.com/ditrit/shoset/msg.Cell).GetMessage + + +GetMessage + + + + + +(*github.com/ditrit/shoset/msg.Iterator).Get + + +Get + + + + + +(*github.com/ditrit/shoset/msg.Iterator).Get->(*github.com/ditrit/shoset/msg.Cell).GetMessage + + + + + + + + +(*github.com/ditrit/shoset/msg.Queue).IsEmpty + + +IsEmpty + + + + + +(*github.com/ditrit/shoset/msg.Iterator).Get->(*github.com/ditrit/shoset/msg.Queue).IsEmpty + + + + + + + + +(*github.com/ditrit/shoset/msg.Queue).First + + +First + + + + + +(*github.com/ditrit/shoset/msg.Iterator).Get->(*github.com/ditrit/shoset/msg.Queue).First + + + + + + + + +(*github.com/ditrit/shoset/msg.Queue).Next + + +Next + + + + + +(*github.com/ditrit/shoset/msg.Iterator).Get->(*github.com/ditrit/shoset/msg.Queue).Next + + + + + + + + +(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + +GetUUID + + + + + +(*github.com/ditrit/shoset/msg.Iterator).Get->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(*github.com/ditrit/shoset/msg.Iterator).GetQueue + + +GetQueue + + + + + +(*github.com/ditrit/shoset/msg.Iterator).PrintQueue + + +PrintQueue + + + + + +(*github.com/ditrit/shoset/msg.Queue).Print + + +Print + + + + + +(*github.com/ditrit/shoset/msg.Iterator).PrintQueue->(*github.com/ditrit/shoset/msg.Queue).Print + + + + + + + + +(*github.com/ditrit/shoset/msg.PkiEvent).SetCommand + + +SetCommand + + + + + +(*github.com/ditrit/shoset/msg.Queue).LockQueue + + +LockQueue + + + + + +(*github.com/ditrit/shoset/msg.Queue).UnlockQueue + + +UnlockQueue + + + + + +(*github.com/ditrit/shoset/msg.Queue).remove + + +remove + + + + + +(*github.com/ditrit/shoset/msg.Queue).remove->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(*github.com/ditrit/shoset/msg.Queue).Push + + +Push + + + + + +(*github.com/ditrit/shoset/msg.Queue).Push$1 + + +Push$1 + + + + + +(*github.com/ditrit/shoset/msg.Queue).Push->(*github.com/ditrit/shoset/msg.Queue).Push$1 + + + + + + + + + + +(*github.com/ditrit/shoset/msg.Queue).Push->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(github.com/ditrit/shoset/msg.MessageBase).GetTimeout + + +GetTimeout + + + + + +(*github.com/ditrit/shoset/msg.Queue).Push->(github.com/ditrit/shoset/msg.MessageBase).GetTimeout + + + + + + + + +(*github.com/ditrit/shoset/msg.Queue).GetByReferencesUUID + + +GetByReferencesUUID + + + + + +(github.com/ditrit/shoset/msg.Event).GetReferenceUUID + + +GetReferenceUUID + + + + + +(*github.com/ditrit/shoset/msg.Queue).GetByReferencesUUID->(github.com/ditrit/shoset/msg.Event).GetReferenceUUID + + + + + + + + +(*github.com/ditrit/shoset/msg.Queue).Print->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(*github.com/ditrit/shoset/msg.Reader).UpdateReader + + +UpdateReader + + + + + +(*github.com/ditrit/shoset/msg.Reader).ReadString + + +ReadString + + + + + +(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + +ReadMessage + + + + + +(*github.com/ditrit/shoset/msg.RoutingEvent).SetNbSteps + + +SetNbSteps + + + + + +(*github.com/ditrit/shoset/msg.Writer).SendMessage + + +SendMessage + + + + + +(*github.com/ditrit/shoset/msg.Writer).WriteString + + +WriteString + + + + + +(*github.com/ditrit/shoset/msg.Writer).SendMessage->(*github.com/ditrit/shoset/msg.Writer).WriteString + + + + + + + + +(*github.com/ditrit/shoset/msg.Writer).WriteMessage + + +WriteMessage + + + + + +(*github.com/ditrit/shoset/msg.Writer).SendMessage->(*github.com/ditrit/shoset/msg.Writer).WriteMessage + + + + + + + + +(github.com/ditrit/shoset/msg.Command).GetMessageType + + +GetMessageType + + + + + +(*github.com/ditrit/shoset/msg.Writer).SendMessage->(github.com/ditrit/shoset/msg.Command).GetMessageType + + + + + + + + +(github.com/ditrit/shoset/msg.Config).GetMessageType + + +GetMessageType + + + + + +(*github.com/ditrit/shoset/msg.Writer).SendMessage->(github.com/ditrit/shoset/msg.Config).GetMessageType + + + + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetMessageType + + +GetMessageType + + + + + +(*github.com/ditrit/shoset/msg.Writer).SendMessage->(github.com/ditrit/shoset/msg.ConfigProtocol).GetMessageType + + + + + + + + +(github.com/ditrit/shoset/msg.Event).GetMessageType + + +GetMessageType + + + + + +(*github.com/ditrit/shoset/msg.Writer).SendMessage->(github.com/ditrit/shoset/msg.Event).GetMessageType + + + + + + + + +(github.com/ditrit/shoset/msg.ForwardAck).GetMessageType + + +GetMessageType + + + + + +(*github.com/ditrit/shoset/msg.Writer).SendMessage->(github.com/ditrit/shoset/msg.ForwardAck).GetMessageType + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetMessageType + + +GetMessageType + + + + + +(*github.com/ditrit/shoset/msg.Writer).SendMessage->(github.com/ditrit/shoset/msg.PkiEvent).GetMessageType + + + + + + + + +(github.com/ditrit/shoset/msg.RoutingEvent).GetMessageType + + +GetMessageType + + + + + +(*github.com/ditrit/shoset/msg.Writer).SendMessage->(github.com/ditrit/shoset/msg.RoutingEvent).GetMessageType + + + + + + + + +(github.com/ditrit/shoset/msg.SimpleMessage).GetMessageType + + +GetMessageType + + + + + +(*github.com/ditrit/shoset/msg.Writer).SendMessage->(github.com/ditrit/shoset/msg.SimpleMessage).GetMessageType + + + + + + + + +(*github.com/ditrit/shoset/msg.Writer).Flush + + +Flush + + + + + +(*github.com/ditrit/shoset/msg.Writer).WriteMessage->(*github.com/ditrit/shoset/msg.Writer).Flush + + + + + + + + +(*github.com/ditrit/shoset/msg.Writer).UpdateWriter + + +UpdateWriter + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay$1$1 + + +(*ConfigJoinHandler).HandleDoubleWay$1$1 + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay$1$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetAddress + + +GetAddress + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay$1$1->(github.com/ditrit/shoset/msg.ConfigProtocol).GetAddress + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay$1$1 + + +(*ConfigLinkHandler).HandleDoubleWay$1$1 + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay$1$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +github.com/ditrit/shoset.sendToBrothers + + +sendToBrothers + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetLogicalName + + +GetLogicalName + + + + + +github.com/ditrit/shoset.sendToBrothers->(github.com/ditrit/shoset/msg.ConfigProtocol).GetLogicalName + + + + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetYourBrothers + + +GetYourBrothers + + + + + +github.com/ditrit/shoset.sendToBrothers->(github.com/ditrit/shoset/msg.ConfigProtocol).GetYourBrothers + + + + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetMyBrothers + + +GetMyBrothers + + + + + +github.com/ditrit/shoset.sendToBrothers->(github.com/ditrit/shoset/msg.ConfigProtocol).GetMyBrothers + + + + + + + + +github.com/ditrit/shoset.NewShoset + + +NewShoset + + + + + +github.com/ditrit/shoset.NewShoset->github.com/ditrit/shoset/msg.NewQueue + + + + + + + + +(*github.com/ditrit/shoset.CommandHandler).Send$1 + + +Send$1 + + + + + +(*github.com/ditrit/shoset.CommandHandler).Send$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.CommandHandler).Wait$1 + + +Wait$1 + + + + + +(*github.com/ditrit/shoset.CommandHandler).Wait$1->(*github.com/ditrit/shoset/msg.Cell).GetMessage + + + + + + + + +(*github.com/ditrit/shoset.CommandHandler).Wait$1->(*github.com/ditrit/shoset/msg.Iterator).Get + + + + + + + + +(github.com/ditrit/shoset/msg.Command).GetCommand + + +GetCommand + + + + + +(*github.com/ditrit/shoset.CommandHandler).Wait$1->(github.com/ditrit/shoset/msg.Command).GetCommand + + + + + + + + +(*github.com/ditrit/shoset.CommandHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.CommandHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.CommandHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.CommandHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Queue).Push + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay$1 + + +HandleDoubleWay$1 + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay$1->(github.com/ditrit/shoset/msg.ConfigProtocol).GetAddress + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay->github.com/ditrit/shoset/msg.NewConfigProtocol + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetAddress + + + + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetCommandName + + +GetCommandName + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetCommandName + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Send$1 + + +Send$1 + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Send$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Wait$1 + + +Wait$1 + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Wait$1->(*github.com/ditrit/shoset/msg.Cell).GetMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Wait$1->(*github.com/ditrit/shoset/msg.Iterator).Get + + + + + + + + +(github.com/ditrit/shoset/msg.Config).GetCommand + + +GetCommand + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Wait$1->(github.com/ditrit/shoset/msg.Config).GetCommand + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.ConfigHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Queue).Push + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->github.com/ditrit/shoset/msg.NewConfigProtocol + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetAddress + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetCommandName + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->github.com/ditrit/shoset/msg.NewConfigProtocol + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->github.com/ditrit/shoset/msg.NewConfigBrothersProtocol + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetAddress + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetCommandName + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetLogicalName + + + + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetShosetType + + +GetShosetType + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetShosetType + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Send$1 + + +Send$1 + + + + + +(*github.com/ditrit/shoset.EventHandler).Send$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(*github.com/ditrit/shoset/msg.Cell).GetMessage + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(*github.com/ditrit/shoset/msg.Iterator).Get + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(*github.com/ditrit/shoset/msg.Iterator).GetQueue + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(*github.com/ditrit/shoset/msg.Queue).LockQueue + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(*github.com/ditrit/shoset/msg.Queue).UnlockQueue + + + + + + + + +(github.com/ditrit/shoset/msg.Event).GetTopic + + +GetTopic + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(github.com/ditrit/shoset/msg.Event).GetTopic + + + + + + + + +(github.com/ditrit/shoset/msg.Event).GetEvent + + +GetEvent + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(github.com/ditrit/shoset/msg.Event).GetEvent + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.EventHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.EventHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Queue).Push + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait->(*github.com/ditrit/shoset/msg.Cell).GetMessage + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait->(*github.com/ditrit/shoset/msg.Iterator).Get + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait->(*github.com/ditrit/shoset/msg.Iterator).GetQueue + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait->(*github.com/ditrit/shoset/msg.Queue).LockQueue + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait->(*github.com/ditrit/shoset/msg.Queue).UnlockQueue + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Queue).Push + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).Send$1 + + +Send$1 + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).Send$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->github.com/ditrit/shoset/msg.NewPkiEventReturn + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.MessageBase).SetUUID + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.PkiEvent).SetCommand + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Queue).Push + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetCommand + + +GetCommand + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.PkiEvent).GetCommand + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetRequestAddress + + +GetRequestAddress + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.PkiEvent).GetRequestAddress + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetCertificateRequest + + +GetCertificateRequest + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.PkiEvent).GetCertificateRequest + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetHostPublicKey + + +GetHostPublicKey + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.PkiEvent).GetHostPublicKey + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).Send$1 + + +Send$1 + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).Send$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(github.com/ditrit/shoset/msg.RoutingEvent).GetOrigin + + +GetOrigin + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.RoutingEvent).GetOrigin + + + + + + + + +(github.com/ditrit/shoset/msg.RoutingEvent).GetNbSteps + + +GetNbSteps + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.RoutingEvent).GetNbSteps + + + + + + + + +(github.com/ditrit/shoset/msg.RoutingEvent).GetRerouteTimestamp + + +GetRerouteTimestamp + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.RoutingEvent).GetRerouteTimestamp + + + + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage + + +forwardMessage + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->github.com/ditrit/shoset/msg.NewRoutingEvent + + + + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(github.com/ditrit/shoset/msg.MessageBase).GetDestinationLname + + +GetDestinationLname + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(github.com/ditrit/shoset/msg.MessageBase).GetDestinationLname + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Send + + +Send + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(github.com/ditrit/shoset/msg.Event).GetMessageType + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(github.com/ditrit/shoset/msg.RoutingEvent).GetMessageType + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(github.com/ditrit/shoset/msg.SimpleMessage).GetMessageType + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.Shoset).Wait->github.com/ditrit/shoset/msg.NewIterator + + + + + + + + +(*github.com/ditrit/shoset.Shoset).EndProtocol + + +EndProtocol + + + + + +(*github.com/ditrit/shoset.Shoset).EndProtocol->github.com/ditrit/shoset/msg.NewConfigProtocol + + + + + + + + +(*github.com/ditrit/shoset.Shoset).EndProtocol->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute + + +SaveRoute + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->github.com/ditrit/shoset/msg.NewRoutingEvent + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(*github.com/ditrit/shoset/msg.RoutingEvent).SetNbSteps + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(github.com/ditrit/shoset/msg.RoutingEvent).GetOrigin + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(github.com/ditrit/shoset/msg.RoutingEvent).GetNbSteps + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(github.com/ditrit/shoset/msg.RoutingEvent).GetRerouteTimestamp + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol + + +Protocol + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->github.com/ditrit/shoset/msg.NewConfigProtocol + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse + + +HandlePkiResponse + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetSignedCert + + +GetSignedCert + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse->(github.com/ditrit/shoset/msg.PkiEvent).GetSignedCert + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetCAcert + + +GetCAcert + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse->(github.com/ditrit/shoset/msg.PkiEvent).GetCAcert + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetCAprivateKey + + +GetCAprivateKey + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse->(github.com/ditrit/shoset/msg.PkiEvent).GetCAprivateKey + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).Store + + +Store + + + + + +(*github.com/ditrit/shoset.ShosetConn).Store->github.com/ditrit/shoset/msg.NewRoutingEvent + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest + + +RunPkiRequest + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->github.com/ditrit/shoset/msg.NewPkiEventInit + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).UpdateConn + + +UpdateConn + + + + + +(*github.com/ditrit/shoset.ShosetConn).UpdateConn->(*github.com/ditrit/shoset/msg.Reader).UpdateReader + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).UpdateConn->(*github.com/ditrit/shoset/msg.Writer).UpdateWriter + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage + + +ReceiveMessage + + + + + +(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage->(*github.com/ditrit/shoset/msg.Reader).ReadString + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType + + +handleMessageType + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->github.com/ditrit/shoset/msg.NewForwardAck + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(github.com/ditrit/shoset/msg.MessageBase).GetDestinationLname + + + + + + + + +(github.com/ditrit/shoset/msg.MessageBase).GetTimestamp + + +GetTimestamp + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(github.com/ditrit/shoset/msg.MessageBase).GetTimestamp + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleSingleWay + + +HandleSingleWay + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleSingleWay->(github.com/ditrit/shoset/msg.PkiEvent).GetCommand + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest + + +HandlePkiRequest + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->github.com/ditrit/shoset/msg.NewPkiEventReturn + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset/msg.PkiEvent).SetCommand + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(github.com/ditrit/shoset/msg.PkiEvent).GetRequestAddress + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(github.com/ditrit/shoset/msg.PkiEvent).GetCertificateRequest + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(github.com/ditrit/shoset/msg.PkiEvent).GetHostPublicKey + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetLogicalName + + +GetLogicalName + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(github.com/ditrit/shoset/msg.PkiEvent).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleConfig + + +HandleConfig + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleConfig->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait->(*github.com/ditrit/shoset/msg.Cell).GetMessage + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait->(*github.com/ditrit/shoset/msg.Iterator).Get + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait->(*github.com/ditrit/shoset/msg.Iterator).GetQueue + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait->(*github.com/ditrit/shoset/msg.Queue).LockQueue + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait->(*github.com/ditrit/shoset/msg.Queue).UnlockQueue + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Queue).Push + + + + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetMessageType->(github.com/ditrit/shoset/msg.ConfigProtocol).GetCommandName + + + + + + + + +(github.com/ditrit/shoset/msg.MessageBase).GetPayload + + +GetPayload + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetMessageType->(github.com/ditrit/shoset/msg.PkiEvent).GetCommand + + + + + + + + +github.com/ditrit/shoset/test.testForwardMessage + + +testForwardMessage + + + + + +github.com/ditrit/shoset/test.testForwardMessage->github.com/ditrit/shoset/msg.NewSimpleMessage + + + + + + + + +github.com/ditrit/shoset/test.testForwardMessageMultiProcess + + +testForwardMessageMultiProcess + + + + + +github.com/ditrit/shoset/test.testForwardMessageMultiProcess->github.com/ditrit/shoset/msg.NewIterator + + + + + + + + +github.com/ditrit/shoset/test.testForwardMessageMultiProcess$2 + + +testForwardMessageMultiProcess$2 + + + + + +github.com/ditrit/shoset/test.testForwardMessageMultiProcess$2->github.com/ditrit/shoset/msg.NewSimpleMessage + + + + + + + + +github.com/ditrit/shoset/test/example.SimpleExample + + +SimpleExample + + + + + +github.com/ditrit/shoset/test/example.SimpleExample->github.com/ditrit/shoset/msg.NewEventClassic + + + + + + + + +github.com/ditrit/shoset/test/example.SimpleExample->(github.com/ditrit/shoset/msg.MessageBase).GetPayload + + + + + + + + diff --git a/uml/call_graph/shoset.svg b/uml/call_graph/shoset.svg new file mode 100644 index 0000000..29c73a7 --- /dev/null +++ b/uml/call_graph/shoset.svg @@ -0,0 +1,7064 @@ + + + + + + +gocallvis + +github.com/ditrit/shoset/test + +cluster_focus + +shoset + + +cluster_*github.com/ditrit/shoset.CommandHandler + + +(*CommandHandler) + + + + +cluster_*github.com/ditrit/shoset.Config + + +(*Config) + + + + +cluster_*github.com/ditrit/shoset.ConfigByeHandler + + +(*ConfigByeHandler) + + + + +cluster_*github.com/ditrit/shoset.ConfigHandler + + +(*ConfigHandler) + + + + +cluster_*github.com/ditrit/shoset.ConfigJoinHandler + + +(*ConfigJoinHandler) + + + + +cluster_*github.com/ditrit/shoset.ConfigLinkHandler + + +(*ConfigLinkHandler) + + + + +cluster_*github.com/ditrit/shoset.EventHandler + + +(*EventHandler) + + + + +cluster_*github.com/ditrit/shoset.ForwardAcknownledgeHandler + + +(*ForwardAcknownledgeHandler) + + + + +cluster_*github.com/ditrit/shoset.MapSyncMap + + +(*MapSyncMap) + + + + +cluster_*github.com/ditrit/shoset.PkiEventHandler + + +(*PkiEventHandler) + + + + +cluster_*github.com/ditrit/shoset.RoutingEventHandler + + +(*RoutingEventHandler) + + + + +cluster_*github.com/ditrit/shoset.Shoset + + +(*Shoset) + + + + +cluster_*github.com/ditrit/shoset.ShosetConn + + +(*ShosetConn) + + + + +cluster_*github.com/ditrit/shoset.SimpleMessageHandler + + +(*SimpleMessageHandler) + + + + +cluster_github.com/ditrit/shoset.Route + + +(Route) + + + + +cluster_github.com/ditrit/shoset/concurent_data + + +concurentData + + + + +cluster_*github.com/ditrit/shoset/concurent_data.ConcurentSlice + + +(*ConcurentSlice) + + + + +cluster_github.com/ditrit/shoset/event_bus + + +eventBus + + + + +cluster_*github.com/ditrit/shoset/event_bus.EventBus + + +(*EventBus) + + + + +cluster_github.com/ditrit/shoset/msg + + +msg + + + + +cluster_*github.com/ditrit/shoset/msg.Cell + + +(*Cell) + + + + +cluster_*github.com/ditrit/shoset/msg.Iterator + + +(*Iterator) + + + + +cluster_*github.com/ditrit/shoset/msg.MessageBase + + +(*MessageBase) + + + + +cluster_*github.com/ditrit/shoset/msg.PkiEvent + + +(*PkiEvent) + + + + +cluster_*github.com/ditrit/shoset/msg.Queue + + +(*Queue) + + + + +cluster_*github.com/ditrit/shoset/msg.Reader + + +(*Reader) + + + + +cluster_*github.com/ditrit/shoset/msg.RoutingEvent + + +(*RoutingEvent) + + + + +cluster_*github.com/ditrit/shoset/msg.Writer + + +(*Writer) + + + + +cluster_github.com/ditrit/shoset/msg.Command + + +(Command) + + + + +cluster_github.com/ditrit/shoset/msg.Config + + +(Config) + + + + +cluster_github.com/ditrit/shoset/msg.ConfigProtocol + + +(ConfigProtocol) + + + + +cluster_github.com/ditrit/shoset/msg.Event + + +(Event) + + + + +cluster_github.com/ditrit/shoset/msg.MessageBase + + +(MessageBase) + + + + +cluster_github.com/ditrit/shoset/msg.PkiEvent + + +(PkiEvent) + + + + +cluster_github.com/ditrit/shoset/msg.RoutingEvent + + +(RoutingEvent) + + + + +cluster_github.com/ditrit/shoset/msg.SimpleMessage + + +(SimpleMessage) + + + + +cluster_github.com/ditrit/shoset/test + + +main + + + + +cluster_github.com/ditrit/shoset/test/example + + +example + + + + +cluster_github.com/ditrit/shoset/test/old_test + + +oldTest + + + + +cluster_github.com/ditrit/shoset/test/utils_for_test + + +utilsForTest + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay$1$1 + + +(*ConfigLinkHandler).HandleDoubleWay$1$1 + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetWriter + + +GetWriter + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay$1$1->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset/msg.Writer).SendMessage + + +SendMessage + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay$1$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay$1$1 + + +(*ConfigJoinHandler).HandleDoubleWay$1$1 + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay$1$1->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay$1$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetAddress + + +GetAddress + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay$1$1->(github.com/ditrit/shoset/msg.ConfigProtocol).GetAddress + + + + + + + + +github.com/ditrit/shoset.Keys$2 + + +Keys$2 + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetDirection + + +GetDirection + + + + + +github.com/ditrit/shoset.Keys$2->(*github.com/ditrit/shoset.ShosetConn).GetDirection + + + + + + + + +(*github.com/ditrit/shoset.MapSyncMap).Iterate$1$1 + + +(*MapSyncMap).Iterate$1$1 + + + + + +(*github.com/ditrit/shoset.CommandHandler).Send$1 + + +Send$1 + + + + + +(*github.com/ditrit/shoset.MapSyncMap).Iterate$1$1->(*github.com/ditrit/shoset.CommandHandler).Send$1 + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay$1 + + +HandleDoubleWay$1 + + + + + +(*github.com/ditrit/shoset.MapSyncMap).Iterate$1$1->(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay$1 + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Send$1 + + +Send$1 + + + + + +(*github.com/ditrit/shoset.MapSyncMap).Iterate$1$1->(*github.com/ditrit/shoset.ConfigHandler).Send$1 + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Send$1 + + +Send$1 + + + + + +(*github.com/ditrit/shoset.MapSyncMap).Iterate$1$1->(*github.com/ditrit/shoset.EventHandler).Send$1 + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).Send$1 + + +Send$1 + + + + + +(*github.com/ditrit/shoset.MapSyncMap).Iterate$1$1->(*github.com/ditrit/shoset.PkiEventHandler).Send$1 + + + + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).Send$1 + + +Send$1 + + + + + +(*github.com/ditrit/shoset.MapSyncMap).Iterate$1$1->(*github.com/ditrit/shoset.RoutingEventHandler).Send$1 + + + + + + + + +(*github.com/ditrit/shoset.Shoset).String$1 + + +String$1 + + + + + +(*github.com/ditrit/shoset.MapSyncMap).Iterate$1$1->(*github.com/ditrit/shoset.Shoset).String$1 + + + + + + + + +github.com/ditrit/shoset.GetIP + + +GetIP + + + + + +github.com/ditrit/shoset.EncodeFile + + +EncodeFile + + + + + +github.com/ditrit/shoset.PrepareCertificate + + +PrepareCertificate + + + + + +github.com/ditrit/shoset.Log + + +Log + + + + + +github.com/ditrit/shoset.PrepareCertificate->github.com/ditrit/shoset.Log + + + + + + + + +github.com/ditrit/shoset.NewShosetConn + + +NewShosetConn + + + + + +github.com/ditrit/shoset.NewShosetConn->github.com/ditrit/shoset.GetIP + + + + + + + + +github.com/ditrit/shoset.GetPrivateKey + + +GetPrivateKey + + + + + +github.com/ditrit/shoset.Keys + + +Keys + + + + + +github.com/ditrit/shoset.sendToBrothers + + +sendToBrothers + + + + + +github.com/ditrit/shoset.sendToBrothers->github.com/ditrit/shoset.NewShosetConn + + + + + + + + +(*github.com/ditrit/shoset.MapSyncMap).StoreConfig + + +StoreConfig + + + + + +github.com/ditrit/shoset.sendToBrothers->(*github.com/ditrit/shoset.MapSyncMap).StoreConfig + + + + + + + + +(*github.com/ditrit/shoset.Shoset).GetLogicalName + + +GetLogicalName + + + + + +github.com/ditrit/shoset.sendToBrothers->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).GetBindAddress + + +GetBindAddress + + + + + +github.com/ditrit/shoset.sendToBrothers->(*github.com/ditrit/shoset.Shoset).GetBindAddress + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol + + +Protocol + + + + + +github.com/ditrit/shoset.sendToBrothers->(*github.com/ditrit/shoset.Shoset).Protocol + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetShoset + + +GetShoset + + + + + +github.com/ditrit/shoset.sendToBrothers->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetRemoteShosetType + + +GetRemoteShosetType + + + + + +github.com/ditrit/shoset.sendToBrothers->(*github.com/ditrit/shoset.ShosetConn).GetRemoteShosetType + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetLocalLogicalName + + +GetLocalLogicalName + + + + + +github.com/ditrit/shoset.sendToBrothers->(*github.com/ditrit/shoset.ShosetConn).GetLocalLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).SetRemoteLogicalName + + +SetRemoteLogicalName + + + + + +github.com/ditrit/shoset.sendToBrothers->(*github.com/ditrit/shoset.ShosetConn).SetRemoteLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).SetRemoteShosetType + + +SetRemoteShosetType + + + + + +github.com/ditrit/shoset.sendToBrothers->(*github.com/ditrit/shoset.ShosetConn).SetRemoteShosetType + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetLocalShosetType + + +GetLocalShosetType + + + + + +github.com/ditrit/shoset.sendToBrothers->(*github.com/ditrit/shoset.ShosetConn).GetLocalShosetType + + + + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetLogicalName + + +GetLogicalName + + + + + +github.com/ditrit/shoset.sendToBrothers->(github.com/ditrit/shoset/msg.ConfigProtocol).GetLogicalName + + + + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetYourBrothers + + +GetYourBrothers + + + + + +github.com/ditrit/shoset.sendToBrothers->(github.com/ditrit/shoset/msg.ConfigProtocol).GetYourBrothers + + + + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetMyBrothers + + +GetMyBrothers + + + + + +github.com/ditrit/shoset.sendToBrothers->(github.com/ditrit/shoset/msg.ConfigProtocol).GetMyBrothers + + + + + + + + +github.com/ditrit/shoset.NewRoute + + +NewRoute + + + + + +github.com/ditrit/shoset.NewShoset + + +NewShoset + + + + + +github.com/ditrit/shoset.NewConfig + + +NewConfig + + + + + +github.com/ditrit/shoset.NewShoset->github.com/ditrit/shoset.NewConfig + + + + + + + + +(*github.com/ditrit/shoset.MapSyncMap).SetConfig + + +SetConfig + + + + + +github.com/ditrit/shoset.NewShoset->(*github.com/ditrit/shoset.MapSyncMap).SetConfig + + + + + + + + +github.com/ditrit/shoset/concurent_data.NewConcurentSlice + + +NewConcurentSlice + + + + + +github.com/ditrit/shoset.NewShoset->github.com/ditrit/shoset/concurent_data.NewConcurentSlice + + + + + + + + +github.com/ditrit/shoset/event_bus.NewEventBus + + +NewEventBus + + + + + +github.com/ditrit/shoset.NewShoset->github.com/ditrit/shoset/event_bus.NewEventBus + + + + + + + + +github.com/ditrit/shoset/msg.NewQueue + + +NewQueue + + + + + +github.com/ditrit/shoset.NewShoset->github.com/ditrit/shoset/msg.NewQueue + + + + + + + + +github.com/ditrit/shoset.InitPrettyLogger + + +InitPrettyLogger + + + + + +github.com/ditrit/shoset.LogWithCaller + + +LogWithCaller + + + + + +github.com/ditrit/shoset.InitPrettyLogger->github.com/ditrit/shoset.LogWithCaller + + + + + + + + +github.com/ditrit/shoset.SetLogLevel + + +SetLogLevel + + + + + +(*github.com/ditrit/shoset.CommandHandler).Send$1->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.CommandHandler).Send$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.CommandHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.CommandHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.CommandHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetRemoteShosetType + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetLocalAddress + + +GetLocalAddress + + + + + +(*github.com/ditrit/shoset.CommandHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetLocalAddress + + + + + + + + +(*github.com/ditrit/shoset/msg.Queue).Push + + +Push + + + + + +(*github.com/ditrit/shoset.CommandHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Queue).Push + + + + + + + + +(*github.com/ditrit/shoset.CommandHandler).Send + + +Send + + + + + +(*github.com/ditrit/shoset.MapSyncMap).Iterate + + +Iterate + + + + + +(*github.com/ditrit/shoset.CommandHandler).Send->(*github.com/ditrit/shoset.MapSyncMap).Iterate + + + + + + + + +(*github.com/ditrit/shoset.CommandHandler).Wait$1 + + +Wait$1 + + + + + +(*github.com/ditrit/shoset/msg.Cell).GetMessage + + +GetMessage + + + + + +(*github.com/ditrit/shoset.CommandHandler).Wait$1->(*github.com/ditrit/shoset/msg.Cell).GetMessage + + + + + + + + +(*github.com/ditrit/shoset/msg.Iterator).Get + + +Get + + + + + +(*github.com/ditrit/shoset.CommandHandler).Wait$1->(*github.com/ditrit/shoset/msg.Iterator).Get + + + + + + + + +(github.com/ditrit/shoset/msg.Command).GetCommand + + +GetCommand + + + + + +(*github.com/ditrit/shoset.CommandHandler).Wait$1->(github.com/ditrit/shoset/msg.Command).GetCommand + + + + + + + + +(*github.com/ditrit/shoset.CommandHandler).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.CommandHandler).Wait->(*github.com/ditrit/shoset.CommandHandler).Wait$1 + + + + + + + + + + +(*github.com/ditrit/shoset.CommandHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetReader + + +GetReader + + + + + +(*github.com/ditrit/shoset.CommandHandler).Get->(*github.com/ditrit/shoset.ShosetConn).GetReader + + + + + + + + +(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + +ReadMessage + + + + + +(*github.com/ditrit/shoset.CommandHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.Config).SetFileName + + +SetFileName + + + + + +(*github.com/ditrit/shoset.Config).InitFolders + + +InitFolders + + + + + +(*github.com/ditrit/shoset.Config).GetFileName + + +GetFileName + + + + + +(*github.com/ditrit/shoset.Config).GetBaseDirectory + + +GetBaseDirectory + + + + + +(*github.com/ditrit/shoset.Config).ReadConfig + + +ReadConfig + + + + + +(*github.com/ditrit/shoset.Config).GetSlice + + +GetSlice + + + + + +(*github.com/ditrit/shoset.Config).DeleteFromKey + + +DeleteFromKey + + + + + +(*github.com/ditrit/shoset.Config).DeleteFromKey->(*github.com/ditrit/shoset.Config).GetSlice + + + + + + + + +(*github.com/ditrit/shoset.Config).AppendToKey + + +AppendToKey + + + + + +(*github.com/ditrit/shoset.Config).AppendToKey->(*github.com/ditrit/shoset.Config).GetSlice + + + + + + + + +(*github.com/ditrit/shoset.Config).WriteConfig + + +WriteConfig + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay$1->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay$1->(github.com/ditrit/shoset/msg.ConfigProtocol).GetAddress + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).Send + + +Send + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).Get->(*github.com/ditrit/shoset.ShosetConn).GetReader + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay->(*github.com/ditrit/shoset.MapSyncMap).Iterate + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).GetShosetType + + +GetShosetType + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).GetShosetType + + + + + + + + +(*github.com/ditrit/shoset.Shoset).DeleteConn + + +DeleteConn + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).DeleteConn + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +github.com/ditrit/shoset/msg.NewConfigProtocol + + +NewConfigProtocol + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay->github.com/ditrit/shoset/msg.NewConfigProtocol + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetAddress + + + + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetCommandName + + +GetCommandName + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetCommandName + + + + + + + + +(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Send$1->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Send$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.ConfigHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetRemoteShosetType + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetLocalAddress + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Queue).Push + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Send + + +Send + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Send->(*github.com/ditrit/shoset.MapSyncMap).Iterate + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Wait$1 + + +Wait$1 + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Wait$1->(*github.com/ditrit/shoset/msg.Cell).GetMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Wait$1->(*github.com/ditrit/shoset/msg.Iterator).Get + + + + + + + + +(github.com/ditrit/shoset/msg.Config).GetCommand + + +GetCommand + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Wait$1->(github.com/ditrit/shoset/msg.Config).GetCommand + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Wait->(*github.com/ditrit/shoset.ConfigHandler).Wait$1 + + + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Get->(*github.com/ditrit/shoset.ShosetConn).GetReader + + + + + + + + +(*github.com/ditrit/shoset.ConfigHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay$1 + + +HandleDoubleWay$1 + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay$1->(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay$1$1 + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).Send + + +Send + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).Get->(*github.com/ditrit/shoset.ShosetConn).GetReader + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).GetBindAddress + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).GetShosetType + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).Protocol + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).DeleteConn + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetRemoteAddress + + +GetRemoteAddress + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).SetRemoteAddress + + +SetRemoteAddress + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).SetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).Store + + +Store + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).Store + + + + + + + + +(*github.com/ditrit/shoset/concurent_data.ConcurentSlice).DeleteFromConcurentSlice + + +DeleteFromConcurentSlice + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(*github.com/ditrit/shoset/concurent_data.ConcurentSlice).DeleteFromConcurentSlice + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->github.com/ditrit/shoset/msg.NewConfigProtocol + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetAddress + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetCommandName + + + + + + + + +(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay$1 + + +HandleDoubleWay$1 + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay$1->(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay$1$1 + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).Send + + +Send + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).Get->(*github.com/ditrit/shoset.ShosetConn).GetReader + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->github.com/ditrit/shoset.Keys + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).GetShosetType + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).DeleteConn + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetRemoteLogicalName + + +GetRemoteLogicalName + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetRemoteLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).SetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).Store + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(*github.com/ditrit/shoset/concurent_data.ConcurentSlice).DeleteFromConcurentSlice + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->github.com/ditrit/shoset/msg.NewConfigProtocol + + + + + + + + +github.com/ditrit/shoset/msg.NewConfigBrothersProtocol + + +NewConfigBrothersProtocol + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->github.com/ditrit/shoset/msg.NewConfigBrothersProtocol + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetAddress + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetCommandName + + + + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetLogicalName + + + + + + + + +(github.com/ditrit/shoset/msg.ConfigProtocol).GetShosetType + + +GetShosetType + + + + + +(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.ConfigProtocol).GetShosetType + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Send$1->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Send$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Send + + +Send + + + + + +(*github.com/ditrit/shoset.EventHandler).Send->(*github.com/ditrit/shoset.MapSyncMap).Iterate + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset/event_bus.EventBus).Subscribe + + +Subscribe + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(*github.com/ditrit/shoset/event_bus.EventBus).Subscribe + + + + + + + + +(*github.com/ditrit/shoset/event_bus.EventBus).UnSubscribe + + +UnSubscribe + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(*github.com/ditrit/shoset/event_bus.EventBus).UnSubscribe + + + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(*github.com/ditrit/shoset/msg.Cell).GetMessage + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(*github.com/ditrit/shoset/msg.Iterator).Get + + + + + + + + +(*github.com/ditrit/shoset/msg.Iterator).GetQueue + + +GetQueue + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(*github.com/ditrit/shoset/msg.Iterator).GetQueue + + + + + + + + +(*github.com/ditrit/shoset/msg.Queue).LockQueue + + +LockQueue + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(*github.com/ditrit/shoset/msg.Queue).LockQueue + + + + + + + + +(*github.com/ditrit/shoset/msg.Queue).UnlockQueue + + +UnlockQueue + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(*github.com/ditrit/shoset/msg.Queue).UnlockQueue + + + + + + + + +(github.com/ditrit/shoset/msg.Event).GetTopic + + +GetTopic + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(github.com/ditrit/shoset/msg.Event).GetTopic + + + + + + + + +(github.com/ditrit/shoset/msg.Event).GetEvent + + +GetEvent + + + + + +(*github.com/ditrit/shoset.EventHandler).Wait->(github.com/ditrit/shoset/msg.Event).GetEvent + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.EventHandler).Get->(*github.com/ditrit/shoset.ShosetConn).GetReader + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.EventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.EventHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetRemoteShosetType + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetLocalAddress + + + + + + + + +(*github.com/ditrit/shoset/event_bus.EventBus).Publish + + +Publish + + + + + +(*github.com/ditrit/shoset.EventHandler).HandleDoubleWay->(*github.com/ditrit/shoset/event_bus.EventBus).Publish + + + + + + + + +(*github.com/ditrit/shoset.EventHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Queue).Push + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Send + + +Send + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait->(*github.com/ditrit/shoset/event_bus.EventBus).Subscribe + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait->(*github.com/ditrit/shoset/event_bus.EventBus).UnSubscribe + + + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait->(*github.com/ditrit/shoset/msg.Cell).GetMessage + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait->(*github.com/ditrit/shoset/msg.Iterator).Get + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait->(*github.com/ditrit/shoset/msg.Iterator).GetQueue + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait->(*github.com/ditrit/shoset/msg.Queue).LockQueue + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait->(*github.com/ditrit/shoset/msg.Queue).UnlockQueue + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Get->(*github.com/ditrit/shoset.ShosetConn).GetReader + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetRemoteShosetType + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetLocalAddress + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).HandleDoubleWay->(*github.com/ditrit/shoset/event_bus.EventBus).Publish + + + + + + + + +(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Queue).Push + + + + + + + + +(*github.com/ditrit/shoset.MapSyncMap).String + + +String + + + + + +(*github.com/ditrit/shoset.MapSyncMap).GetConfig + + +GetConfig + + + + + +(*github.com/ditrit/shoset.MapSyncMap).LoadValueFromKeys + + +LoadValueFromKeys + + + + + +(*github.com/ditrit/shoset.MapSyncMap).DeleteValueFromKeys + + +DeleteValueFromKeys + + + + + +(*github.com/ditrit/shoset.MapSyncMap).AppendToKeys + + +AppendToKeys + + + + + +(*github.com/ditrit/shoset.MapSyncMap).StoreConfig->github.com/ditrit/shoset.Keys + + + + + + + + +(*github.com/ditrit/shoset.MapSyncMap).updateFile + + +updateFile + + + + + +(*github.com/ditrit/shoset.MapSyncMap).updateFile->(*github.com/ditrit/shoset.Config).GetFileName + + + + + + + + +(*github.com/ditrit/shoset.MapSyncMap).updateFile->(*github.com/ditrit/shoset.Config).AppendToKey + + + + + + + + +(*github.com/ditrit/shoset.MapSyncMap).updateFile->(*github.com/ditrit/shoset.Config).WriteConfig + + + + + + + + +(*github.com/ditrit/shoset.MapSyncMap).Keys + + +Keys + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).Send$1->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).Send$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).Get->(*github.com/ditrit/shoset.ShosetConn).GetReader + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).Send + + +Send + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).Send->(*github.com/ditrit/shoset.MapSyncMap).Iterate + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Config).GetFileName + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Config).GetBaseDirectory + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.MapSyncMap).GetConfig + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.PkiEventHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.Shoset).GetIsPki + + +GetIsPki + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).GetIsPki + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SignCertificate + + +SignCertificate + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).SignCertificate + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetRemoteShosetType + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetLocalAddress + + + + + + + + +github.com/ditrit/shoset/msg.NewPkiEventReturn + + +NewPkiEventReturn + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->github.com/ditrit/shoset/msg.NewPkiEventReturn + + + + + + + + +(*github.com/ditrit/shoset/msg.MessageBase).SetUUID + + +SetUUID + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.MessageBase).SetUUID + + + + + + + + +(*github.com/ditrit/shoset/msg.PkiEvent).SetCommand + + +SetCommand + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.PkiEvent).SetCommand + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Queue).Push + + + + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + +GetUUID + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetCommand + + +GetCommand + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.PkiEvent).GetCommand + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetRequestAddress + + +GetRequestAddress + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.PkiEvent).GetRequestAddress + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetCertificateRequest + + +GetCertificateRequest + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.PkiEvent).GetCertificateRequest + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetHostPublicKey + + +GetHostPublicKey + + + + + +(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.PkiEvent).GetHostPublicKey + + + + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).Send$1->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).Send$1->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).Send + + +Send + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).Send->(*github.com/ditrit/shoset.MapSyncMap).Iterate + + + + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).Get->(*github.com/ditrit/shoset.ShosetConn).GetReader + + + + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute + + +SaveRoute + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.Shoset).SaveRoute + + + + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetIsValid + + +GetIsValid + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetIsValid + + + + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetLocalLogicalName + + + + + + + + +(github.com/ditrit/shoset.Route).GetUUID + + +GetUUID + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(github.com/ditrit/shoset.Route).GetUUID + + + + + + + + +(github.com/ditrit/shoset.Route).GetNbSteps + + +GetNbSteps + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(github.com/ditrit/shoset.Route).GetNbSteps + + + + + + + + +(github.com/ditrit/shoset.Route).GetTimestamp + + +GetTimestamp + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(github.com/ditrit/shoset.Route).GetTimestamp + + + + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(github.com/ditrit/shoset/msg.RoutingEvent).GetOrigin + + +GetOrigin + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.RoutingEvent).GetOrigin + + + + + + + + +(github.com/ditrit/shoset/msg.RoutingEvent).GetNbSteps + + +GetNbSteps + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.RoutingEvent).GetNbSteps + + + + + + + + +(github.com/ditrit/shoset/msg.RoutingEvent).GetRerouteTimestamp + + +GetRerouteTimestamp + + + + + +(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay->(github.com/ditrit/shoset/msg.RoutingEvent).GetRerouteTimestamp + + + + + + + + +(*github.com/ditrit/shoset.Shoset).String$2 + + +String$2 + + + + + +(*github.com/ditrit/shoset.Shoset).String$2->(*github.com/ditrit/shoset.ShosetConn).GetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.Shoset).DeleteConn$1 + + +DeleteConn$1 + + + + + +(*github.com/ditrit/shoset.Shoset).DeleteConn$1->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(github.com/ditrit/shoset.Route).GetNeighborConn + + +GetNeighborConn + + + + + +(*github.com/ditrit/shoset.Shoset).DeleteConn$1->(github.com/ditrit/shoset.Route).GetNeighborConn + + + + + + + + +(*github.com/ditrit/shoset.Shoset).String + + +String + + + + + +(*github.com/ditrit/shoset.Shoset).String->(*github.com/ditrit/shoset.MapSyncMap).Iterate + + + + + + + + +(*github.com/ditrit/shoset.Shoset).String->(*github.com/ditrit/shoset.MapSyncMap).String + + + + + + + + +(*github.com/ditrit/shoset.Shoset).String->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).String->(*github.com/ditrit/shoset.Shoset).GetBindAddress + + + + + + + + +(*github.com/ditrit/shoset.Shoset).String->(*github.com/ditrit/shoset.Shoset).GetShosetType + + + + + + + + +(*github.com/ditrit/shoset.Shoset).String->(*github.com/ditrit/shoset.Shoset).GetIsPki + + + + + + + + +(*github.com/ditrit/shoset/concurent_data.ConcurentSlice).String + + +String + + + + + +(*github.com/ditrit/shoset.Shoset).String->(*github.com/ditrit/shoset/concurent_data.ConcurentSlice).String + + + + + + + + +(*github.com/ditrit/shoset.Shoset).InitPKI + + +InitPKI + + + + + +(*github.com/ditrit/shoset.Shoset).InitPKI->github.com/ditrit/shoset.GetIP + + + + + + + + +(*github.com/ditrit/shoset.Shoset).InitPKI->(*github.com/ditrit/shoset.Config).SetFileName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).InitPKI->(*github.com/ditrit/shoset.MapSyncMap).GetConfig + + + + + + + + +(*github.com/ditrit/shoset.Shoset).InitPKI->(*github.com/ditrit/shoset.Shoset).GetBindAddress + + + + + + + + +(*github.com/ditrit/shoset.Shoset).IsCertified + + +IsCertified + + + + + +(*github.com/ditrit/shoset.Shoset).InitPKI->(*github.com/ditrit/shoset.Shoset).IsCertified + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SetUpDoubleWay + + +SetUpDoubleWay + + + + + +(*github.com/ditrit/shoset.Shoset).InitPKI->(*github.com/ditrit/shoset.Shoset).SetUpDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Bind + + +Bind + + + + + +(*github.com/ditrit/shoset.Shoset).InitPKI->(*github.com/ditrit/shoset.Shoset).Bind + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SetIsPki + + +SetIsPki + + + + + +(*github.com/ditrit/shoset.Shoset).IsCertified->(*github.com/ditrit/shoset.Shoset).SetIsPki + + + + + + + + +(*github.com/ditrit/shoset.Shoset).initCA + + +initCA + + + + + +(*github.com/ditrit/shoset.Shoset).initCA->github.com/ditrit/shoset.EncodeFile + + + + + + + + +(*github.com/ditrit/shoset.Shoset).initCA->(*github.com/ditrit/shoset.Config).InitFolders + + + + + + + + +(*github.com/ditrit/shoset.Shoset).initCA->(*github.com/ditrit/shoset.Config).GetFileName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).initCA->(*github.com/ditrit/shoset.MapSyncMap).GetConfig + + + + + + + + +(*github.com/ditrit/shoset.Shoset).initCA->(*github.com/ditrit/shoset.Shoset).SetIsPki + + + + + + + + +(*github.com/ditrit/shoset.Shoset).initShoset + + +initShoset + + + + + +(*github.com/ditrit/shoset.Shoset).initShoset->github.com/ditrit/shoset.EncodeFile + + + + + + + + +(*github.com/ditrit/shoset.Shoset).initShoset->github.com/ditrit/shoset.PrepareCertificate + + + + + + + + +(*github.com/ditrit/shoset.Shoset).initShoset->(*github.com/ditrit/shoset.Config).GetFileName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).initShoset->(*github.com/ditrit/shoset.Config).GetBaseDirectory + + + + + + + + +(*github.com/ditrit/shoset.Shoset).initShoset->(*github.com/ditrit/shoset.MapSyncMap).GetConfig + + + + + + + + +(*github.com/ditrit/shoset.Shoset).initShoset->(*github.com/ditrit/shoset.Shoset).SignCertificate + + + + + + + + +(*github.com/ditrit/shoset.Shoset).initShoset->(*github.com/ditrit/shoset.Shoset).SetUpDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SignCertificate->(*github.com/ditrit/shoset.Config).GetFileName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SignCertificate->(*github.com/ditrit/shoset.Config).GetBaseDirectory + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SignCertificate->(*github.com/ditrit/shoset.MapSyncMap).GetConfig + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SignCertificate->(*github.com/ditrit/shoset.Shoset).GetIsPki + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SetUpDoubleWay->(*github.com/ditrit/shoset.Config).GetFileName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SetUpDoubleWay->(*github.com/ditrit/shoset.Config).GetBaseDirectory + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SetUpDoubleWay->(*github.com/ditrit/shoset.MapSyncMap).GetConfig + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Bind->github.com/ditrit/shoset.GetIP + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Bind->(*github.com/ditrit/shoset.Config).GetFileName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Bind->(*github.com/ditrit/shoset.Config).ReadConfig + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Bind->(*github.com/ditrit/shoset.Config).GetSlice + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Bind->(*github.com/ditrit/shoset.MapSyncMap).GetConfig + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SetBindAddress + + +SetBindAddress + + + + + +(*github.com/ditrit/shoset.Shoset).Bind->(*github.com/ditrit/shoset.Shoset).SetBindAddress + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Bind->(*github.com/ditrit/shoset.Shoset).Protocol + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SetListener + + +SetListener + + + + + +(*github.com/ditrit/shoset.Shoset).Bind->(*github.com/ditrit/shoset.Shoset).SetListener + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->github.com/ditrit/shoset.GetIP + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->github.com/ditrit/shoset.NewShosetConn + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->(*github.com/ditrit/shoset.Config).SetFileName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->(*github.com/ditrit/shoset.Config).InitFolders + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->(*github.com/ditrit/shoset.MapSyncMap).GetConfig + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->(*github.com/ditrit/shoset.Shoset).GetBindAddress + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->(*github.com/ditrit/shoset.Shoset).GetShosetType + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->(*github.com/ditrit/shoset.Shoset).IsCertified + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->(*github.com/ditrit/shoset.Shoset).SetUpDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->(*github.com/ditrit/shoset.Shoset).Bind + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Certify + + +Certify + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->(*github.com/ditrit/shoset.Shoset).Certify + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->(*github.com/ditrit/shoset.ShosetConn).GetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleConfig + + +HandleConfig + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->(*github.com/ditrit/shoset.ShosetConn).HandleConfig + + + + + + + + + + +(*github.com/ditrit/shoset/concurent_data.ConcurentSlice).AppendToConcurentSlice + + +AppendToConcurentSlice + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->(*github.com/ditrit/shoset/concurent_data.ConcurentSlice).AppendToConcurentSlice + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Protocol->github.com/ditrit/shoset/msg.NewConfigProtocol + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Certify->github.com/ditrit/shoset.NewShosetConn + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest + + +RunPkiRequest + + + + + +(*github.com/ditrit/shoset.Shoset).Certify->(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest + + + + + + + + +(*github.com/ditrit/shoset.Shoset).DeleteConn->(*github.com/ditrit/shoset.Config).DeleteFromKey + + + + + + + + +(*github.com/ditrit/shoset.Shoset).DeleteConn->(*github.com/ditrit/shoset.MapSyncMap).LoadValueFromKeys + + + + + + + + +(*github.com/ditrit/shoset.Shoset).DeleteConn->(*github.com/ditrit/shoset.MapSyncMap).DeleteValueFromKeys + + + + + + + + +(*github.com/ditrit/shoset.Shoset).DeleteConn->(*github.com/ditrit/shoset.ShosetConn).GetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.Shoset).DeleteConn->(*github.com/ditrit/shoset.ShosetConn).GetRemoteShosetType + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetProtocol + + +GetProtocol + + + + + +(*github.com/ditrit/shoset.Shoset).DeleteConn->(*github.com/ditrit/shoset.ShosetConn).GetProtocol + + + + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage + + +forwardMessage + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Send + + +Send + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(*github.com/ditrit/shoset.Shoset).Send + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(*github.com/ditrit/shoset.Shoset).Wait + + + + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(*github.com/ditrit/shoset.ShosetConn).GetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(github.com/ditrit/shoset.Route).GetNeighborConn + + + + + + + + +(github.com/ditrit/shoset.Route).GetNeighbour + + +GetNeighbour + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(github.com/ditrit/shoset.Route).GetNeighbour + + + + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(*github.com/ditrit/shoset/event_bus.EventBus).Subscribe + + + + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(*github.com/ditrit/shoset/event_bus.EventBus).UnSubscribe + + + + + + + + +github.com/ditrit/shoset/msg.NewRoutingEvent + + +NewRoutingEvent + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->github.com/ditrit/shoset/msg.NewRoutingEvent + + + + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(github.com/ditrit/shoset/msg.MessageBase).GetDestinationLname + + +GetDestinationLname + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(github.com/ditrit/shoset/msg.MessageBase).GetDestinationLname + + + + + + + + +(*github.com/ditrit/shoset.Shoset).forwardMessage->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(*github.com/ditrit/shoset.CommandHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(*github.com/ditrit/shoset.ConfigByeHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(*github.com/ditrit/shoset.ConfigHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(*github.com/ditrit/shoset.ConfigJoinHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(*github.com/ditrit/shoset.ConfigLinkHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(*github.com/ditrit/shoset.EventHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(*github.com/ditrit/shoset.PkiEventHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(*github.com/ditrit/shoset.RoutingEventHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Send + + +Send + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(*github.com/ditrit/shoset.SimpleMessageHandler).Send + + + + + + + + +(github.com/ditrit/shoset/msg.Event).GetMessageType + + +GetMessageType + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(github.com/ditrit/shoset/msg.Event).GetMessageType + + + + + + + + +(github.com/ditrit/shoset/msg.RoutingEvent).GetMessageType + + +GetMessageType + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(github.com/ditrit/shoset/msg.RoutingEvent).GetMessageType + + + + + + + + +(github.com/ditrit/shoset/msg.SimpleMessage).GetMessageType + + +GetMessageType + + + + + +(*github.com/ditrit/shoset.Shoset).Send->(github.com/ditrit/shoset/msg.SimpleMessage).GetMessageType + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Wait->(*github.com/ditrit/shoset.CommandHandler).Wait + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Wait->(*github.com/ditrit/shoset.ConfigByeHandler).Wait + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Wait->(*github.com/ditrit/shoset.ConfigHandler).Wait + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Wait->(*github.com/ditrit/shoset.ConfigJoinHandler).Wait + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Wait->(*github.com/ditrit/shoset.ConfigLinkHandler).Wait + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Wait->(*github.com/ditrit/shoset.EventHandler).Wait + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Wait->(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Wait + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Wait->(*github.com/ditrit/shoset.PkiEventHandler).Wait + + + + + + + + +(*github.com/ditrit/shoset.Shoset).Wait->(*github.com/ditrit/shoset.RoutingEventHandler).Wait + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait + + +Wait + + + + + +(*github.com/ditrit/shoset.Shoset).Wait->(*github.com/ditrit/shoset.SimpleMessageHandler).Wait + + + + + + + + +github.com/ditrit/shoset/msg.NewIterator + + +NewIterator + + + + + +(*github.com/ditrit/shoset.Shoset).Wait->github.com/ditrit/shoset/msg.NewIterator + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->github.com/ditrit/shoset.NewRoute + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(*github.com/ditrit/shoset.Shoset).Send + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(*github.com/ditrit/shoset.ShosetConn).GetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(*github.com/ditrit/shoset.ShosetConn).GetRemoteLogicalName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(*github.com/ditrit/shoset.ShosetConn).GetLocalLogicalName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(*github.com/ditrit/shoset/event_bus.EventBus).Publish + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->github.com/ditrit/shoset/msg.NewRoutingEvent + + + + + + + + +(*github.com/ditrit/shoset/msg.RoutingEvent).SetNbSteps + + +SetNbSteps + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(*github.com/ditrit/shoset/msg.RoutingEvent).SetNbSteps + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(github.com/ditrit/shoset/msg.RoutingEvent).GetOrigin + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(github.com/ditrit/shoset/msg.RoutingEvent).GetNbSteps + + + + + + + + +(*github.com/ditrit/shoset.Shoset).SaveRoute->(github.com/ditrit/shoset/msg.RoutingEvent).GetRerouteTimestamp + + + + + + + + +(*github.com/ditrit/shoset.Shoset).GetTlsConfigDoubleWay + + +GetTlsConfigDoubleWay + + + + + +(*github.com/ditrit/shoset.Shoset).handleBind + + +handleBind + + + + + +(*github.com/ditrit/shoset.Shoset).handleBind->github.com/ditrit/shoset.NewShosetConn + + + + + + + + +(*github.com/ditrit/shoset.Shoset).handleBind->(*github.com/ditrit/shoset.Shoset).GetTlsConfigDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.Shoset).GetListener + + +GetListener + + + + + +(*github.com/ditrit/shoset.Shoset).handleBind->(*github.com/ditrit/shoset.Shoset).GetListener + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).UpdateConn + + +UpdateConn + + + + + +(*github.com/ditrit/shoset.Shoset).handleBind->(*github.com/ditrit/shoset.ShosetConn).UpdateConn + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetConn + + +GetConn + + + + + +(*github.com/ditrit/shoset.Shoset).handleBind->(*github.com/ditrit/shoset.ShosetConn).GetConn + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunInConnSingle + + +RunInConnSingle + + + + + +(*github.com/ditrit/shoset.Shoset).handleBind->(*github.com/ditrit/shoset.ShosetConn).RunInConnSingle + + + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunInConnDouble + + +RunInConnDouble + + + + + +(*github.com/ditrit/shoset.Shoset).handleBind->(*github.com/ditrit/shoset.ShosetConn).RunInConnDouble + + + + + + + + + + +(*github.com/ditrit/shoset.Shoset).WaitForProtocols + + +WaitForProtocols + + + + + +(*github.com/ditrit/shoset.Shoset).WaitForProtocols->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).WaitForProtocols->(*github.com/ditrit/shoset/concurent_data.ConcurentSlice).String + + + + + + + + +(*github.com/ditrit/shoset/concurent_data.ConcurentSlice).WaitForEmpty + + +WaitForEmpty + + + + + +(*github.com/ditrit/shoset.Shoset).WaitForProtocols->(*github.com/ditrit/shoset/concurent_data.ConcurentSlice).WaitForEmpty + + + + + + + + +(*github.com/ditrit/shoset.Shoset).EndProtocol + + +EndProtocol + + + + + +(*github.com/ditrit/shoset.Shoset).EndProtocol->(*github.com/ditrit/shoset.MapSyncMap).LoadValueFromKeys + + + + + + + + +(*github.com/ditrit/shoset.Shoset).EndProtocol->(*github.com/ditrit/shoset.MapSyncMap).AppendToKeys + + + + + + + + +(*github.com/ditrit/shoset.Shoset).EndProtocol->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.Shoset).EndProtocol->(*github.com/ditrit/shoset.Shoset).GetBindAddress + + + + + + + + +(*github.com/ditrit/shoset.Shoset).EndProtocol->(*github.com/ditrit/shoset.Shoset).GetShosetType + + + + + + + + +(*github.com/ditrit/shoset.Shoset).EndProtocol->(*github.com/ditrit/shoset.Shoset).DeleteConn + + + + + + + + +(*github.com/ditrit/shoset.Shoset).EndProtocol->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.Shoset).EndProtocol->github.com/ditrit/shoset/msg.NewConfigProtocol + + + + + + + + +(*github.com/ditrit/shoset.Shoset).EndProtocol->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.Shoset).GetConnsByTypeArray + + +GetConnsByTypeArray + + + + + +(*github.com/ditrit/shoset.Shoset).GetConnsByTypeArray->github.com/ditrit/shoset.Keys + + + + + + + + +(*github.com/ditrit/shoset.Shoset).GetConnsByTypeArray->(*github.com/ditrit/shoset.MapSyncMap).Keys + + + + + + + + +(*github.com/ditrit/shoset.Shoset).GenerateSecret + + +GenerateSecret + + + + + +(*github.com/ditrit/shoset.Shoset).GenerateSecret->(*github.com/ditrit/shoset.Shoset).GetIsPki + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).String + + +String + + + + + +(*github.com/ditrit/shoset.ShosetConn).String->(*github.com/ditrit/shoset.ShosetConn).GetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).String->(*github.com/ditrit/shoset.ShosetConn).GetDirection + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).String->(*github.com/ditrit/shoset.ShosetConn).GetRemoteLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).String->(*github.com/ditrit/shoset.ShosetConn).GetRemoteShosetType + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).String->(*github.com/ditrit/shoset.ShosetConn).GetProtocol + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).String->(*github.com/ditrit/shoset.ShosetConn).GetIsValid + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetLocalAddress->(*github.com/ditrit/shoset.Shoset).GetBindAddress + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetLocalAddress->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->github.com/ditrit/shoset.EncodeFile + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->github.com/ditrit/shoset.PrepareCertificate + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->(*github.com/ditrit/shoset.Config).GetFileName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->(*github.com/ditrit/shoset.Config).GetBaseDirectory + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->(*github.com/ditrit/shoset.MapSyncMap).GetConfig + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->(*github.com/ditrit/shoset.ShosetConn).GetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->(*github.com/ditrit/shoset.ShosetConn).UpdateConn + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage + + +ReceiveMessage + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->(*github.com/ditrit/shoset.ShosetConn).GetConn + + + + + + + + +github.com/ditrit/shoset/msg.NewPkiEventInit + + +NewPkiEventInit + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->github.com/ditrit/shoset/msg.NewPkiEventInit + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunPkiRequest->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).UpdateConn->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).UpdateConn->(*github.com/ditrit/shoset.ShosetConn).GetReader + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).SetConn + + +SetConn + + + + + +(*github.com/ditrit/shoset.ShosetConn).UpdateConn->(*github.com/ditrit/shoset.ShosetConn).SetConn + + + + + + + + +(*github.com/ditrit/shoset/msg.Reader).UpdateReader + + +UpdateReader + + + + + +(*github.com/ditrit/shoset.ShosetConn).UpdateConn->(*github.com/ditrit/shoset/msg.Reader).UpdateReader + + + + + + + + +(*github.com/ditrit/shoset/msg.Writer).UpdateWriter + + +UpdateWriter + + + + + +(*github.com/ditrit/shoset.ShosetConn).UpdateConn->(*github.com/ditrit/shoset/msg.Writer).UpdateWriter + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage->(*github.com/ditrit/shoset.Shoset).DeleteConn + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage->(*github.com/ditrit/shoset.ShosetConn).GetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage->(*github.com/ditrit/shoset.ShosetConn).GetDirection + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage->(*github.com/ditrit/shoset.ShosetConn).GetRemoteLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage->(*github.com/ditrit/shoset.ShosetConn).GetReader + + + + + + + + +(*github.com/ditrit/shoset/msg.Reader).ReadString + + +ReadString + + + + + +(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage->(*github.com/ditrit/shoset/msg.Reader).ReadString + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType + + +handleMessageType + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.CommandHandler).HandleDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.CommandHandler).Get + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ConfigByeHandler).Get + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ConfigByeHandler).HandleDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ConfigHandler).HandleDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ConfigHandler).Get + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ConfigJoinHandler).Get + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ConfigJoinHandler).HandleDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ConfigLinkHandler).Get + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ConfigLinkHandler).HandleDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.EventHandler).Get + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.EventHandler).HandleDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Get + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).HandleDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.PkiEventHandler).Get + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.PkiEventHandler).HandleDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.RoutingEventHandler).Get + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.RoutingEventHandler).HandleDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.Shoset).DeleteConn + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ShosetConn).GetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ShosetConn).GetDirection + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ShosetConn).GetRemoteLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ShosetConn).GetLocalLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleSingleWay + + +HandleSingleWay + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.ShosetConn).HandleSingleWay + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Get + + +Get + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.SimpleMessageHandler).Get + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).HandleDoubleWay + + +HandleDoubleWay + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset.SimpleMessageHandler).HandleDoubleWay + + + + + + + + +github.com/ditrit/shoset/msg.NewForwardAck + + +NewForwardAck + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->github.com/ditrit/shoset/msg.NewForwardAck + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(github.com/ditrit/shoset/msg.MessageBase).GetDestinationLname + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(github.com/ditrit/shoset/msg.MessageBase).GetUUID + + + + + + + + +(github.com/ditrit/shoset/msg.MessageBase).GetTimestamp + + +GetTimestamp + + + + + +(*github.com/ditrit/shoset.ShosetConn).handleMessageType->(github.com/ditrit/shoset/msg.MessageBase).GetTimestamp + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetLocalLogicalName->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetLocalLogicalName->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest + + +HandlePkiRequest + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleSingleWay->(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse + + +HandlePkiResponse + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleSingleWay->(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleSingleWay->(github.com/ditrit/shoset/msg.PkiEvent).GetCommand + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->github.com/ditrit/shoset.GetPrivateKey + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.CommandHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.Config).GetFileName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.Config).GetBaseDirectory + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.ConfigByeHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.ConfigHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.ConfigJoinHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.ConfigLinkHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.EventHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.ForwardAcknownledgeHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.MapSyncMap).GetConfig + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.PkiEventHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.RoutingEventHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.Shoset).GetIsPki + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.Shoset).SignCertificate + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset.SimpleMessageHandler).Send + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->github.com/ditrit/shoset/msg.NewPkiEventReturn + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset/msg.PkiEvent).SetCommand + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(github.com/ditrit/shoset/msg.PkiEvent).GetRequestAddress + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(github.com/ditrit/shoset/msg.PkiEvent).GetCertificateRequest + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(github.com/ditrit/shoset/msg.PkiEvent).GetHostPublicKey + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetLogicalName + + +GetLogicalName + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiRequest->(github.com/ditrit/shoset/msg.PkiEvent).GetLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse->github.com/ditrit/shoset.EncodeFile + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse->(*github.com/ditrit/shoset.Config).GetFileName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse->(*github.com/ditrit/shoset.Config).GetBaseDirectory + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse->(*github.com/ditrit/shoset.MapSyncMap).GetConfig + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse->(*github.com/ditrit/shoset.Shoset).SetIsPki + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse->(*github.com/ditrit/shoset.Shoset).SetUpDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetSignedCert + + +GetSignedCert + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse->(github.com/ditrit/shoset/msg.PkiEvent).GetSignedCert + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetCAcert + + +GetCAcert + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse->(github.com/ditrit/shoset/msg.PkiEvent).GetCAcert + + + + + + + + +(github.com/ditrit/shoset/msg.PkiEvent).GetCAprivateKey + + +GetCAprivateKey + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandlePkiResponse->(github.com/ditrit/shoset/msg.PkiEvent).GetCAprivateKey + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).Store->(*github.com/ditrit/shoset.MapSyncMap).AppendToKeys + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).Store->(*github.com/ditrit/shoset.MapSyncMap).StoreConfig + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).Store->(*github.com/ditrit/shoset.Shoset).Send + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).Store->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).Store->(*github.com/ditrit/shoset.ShosetConn).GetLocalLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).SetProtocol + + +SetProtocol + + + + + +(*github.com/ditrit/shoset.ShosetConn).Store->(*github.com/ditrit/shoset.ShosetConn).SetProtocol + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).Store->(*github.com/ditrit/shoset.ShosetConn).SetRemoteLogicalName + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).Store->(*github.com/ditrit/shoset.ShosetConn).SetRemoteShosetType + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).SetIsValid + + +SetIsValid + + + + + +(*github.com/ditrit/shoset.ShosetConn).Store->(*github.com/ditrit/shoset.ShosetConn).SetIsValid + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).Store->github.com/ditrit/shoset/msg.NewRoutingEvent + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetLocalShosetType->(*github.com/ditrit/shoset.Shoset).GetShosetType + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).GetLocalShosetType->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleConfig$1 + + +HandleConfig$1 + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleConfig$1->(*github.com/ditrit/shoset.ShosetConn).GetConn + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleConfig->(*github.com/ditrit/shoset.Shoset).GetTlsConfigDoubleWay + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleConfig->(*github.com/ditrit/shoset.ShosetConn).GetRemoteAddress + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleConfig->(*github.com/ditrit/shoset.ShosetConn).GetWriter + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleConfig->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleConfig->(*github.com/ditrit/shoset.ShosetConn).UpdateConn + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleConfig->(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleConfig->(*github.com/ditrit/shoset.ShosetConn).HandleConfig$1 + + + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).HandleConfig->(*github.com/ditrit/shoset/msg.Writer).SendMessage + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunInConnSingle->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunInConnSingle->(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunInConnDouble$1 + + +RunInConnDouble$1 + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunInConnDouble$1->(*github.com/ditrit/shoset.ShosetConn).GetConn + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunInConnDouble->(*github.com/ditrit/shoset.ShosetConn).ReceiveMessage + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).RunInConnDouble->(*github.com/ditrit/shoset.ShosetConn).RunInConnDouble$1 + + + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).SetLocalAddress + + +SetLocalAddress + + + + + +(*github.com/ditrit/shoset.ShosetConn).SetLocalAddress->(*github.com/ditrit/shoset.Shoset).SetBindAddress + + + + + + + + +(*github.com/ditrit/shoset.ShosetConn).SetLocalAddress->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait->(*github.com/ditrit/shoset/event_bus.EventBus).Subscribe + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait->(*github.com/ditrit/shoset/event_bus.EventBus).UnSubscribe + + + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait->(*github.com/ditrit/shoset/msg.Cell).GetMessage + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait->(*github.com/ditrit/shoset/msg.Iterator).Get + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait->(*github.com/ditrit/shoset/msg.Iterator).GetQueue + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait->(*github.com/ditrit/shoset/msg.Queue).LockQueue + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Wait->(*github.com/ditrit/shoset/msg.Queue).UnlockQueue + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Get->(*github.com/ditrit/shoset.ShosetConn).GetReader + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).Get->(*github.com/ditrit/shoset/msg.Reader).ReadMessage + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetShoset + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetRemoteShosetType + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).HandleDoubleWay->(*github.com/ditrit/shoset.ShosetConn).GetLocalAddress + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).HandleDoubleWay->(*github.com/ditrit/shoset/event_bus.EventBus).Publish + + + + + + + + +(*github.com/ditrit/shoset.SimpleMessageHandler).HandleDoubleWay->(*github.com/ditrit/shoset/msg.Queue).Push + + + + + + + + +github.com/ditrit/shoset/test.testForwardMessage$1 + + +testForwardMessage$1 + + + + + +github.com/ditrit/shoset/test.testForwardMessage$1->(*github.com/ditrit/shoset.Shoset).Wait + + + + + + + + +github.com/ditrit/shoset/test.testForwardMessageMultiProcess + + +testForwardMessageMultiProcess + + + + + +github.com/ditrit/shoset/test.testForwardMessageMultiProcess->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +github.com/ditrit/shoset/test.testForwardMessageMultiProcess->(*github.com/ditrit/shoset.Shoset).WaitForProtocols + + + + + + + + +github.com/ditrit/shoset/test.testForwardMessageMultiProcess$1 + + +testForwardMessageMultiProcess$1 + + + + + +github.com/ditrit/shoset/test.testForwardMessageMultiProcess$1->(*github.com/ditrit/shoset.Shoset).Wait + + + + + + + + +github.com/ditrit/shoset/test.testForwardMessageMultiProcess$2 + + +testForwardMessageMultiProcess$2 + + + + + +github.com/ditrit/shoset/test.testForwardMessageMultiProcess$2->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +github.com/ditrit/shoset/test.testForwardMessageMultiProcess$2->(*github.com/ditrit/shoset.Shoset).Send + + + + + + + + +github.com/ditrit/shoset/test.main + + +main + + + + + +github.com/ditrit/shoset/test.main->github.com/ditrit/shoset.Log + + + + + + + + +github.com/ditrit/shoset/test.main->github.com/ditrit/shoset.InitPrettyLogger + + + + + + + + +github.com/ditrit/shoset/test.main->github.com/ditrit/shoset.SetLogLevel + + + + + + + + +github.com/ditrit/shoset/test.testForwardMessage + + +testForwardMessage + + + + + +github.com/ditrit/shoset/test.testForwardMessage->(*github.com/ditrit/shoset.Shoset).GetLogicalName + + + + + + + + +github.com/ditrit/shoset/test.testForwardMessage->(*github.com/ditrit/shoset.Shoset).Send + + + + + + + + +github.com/ditrit/shoset/test/example.SimpleExample + + +SimpleExample + + + + + +github.com/ditrit/shoset/test/example.SimpleExample->github.com/ditrit/shoset.Log + + + + + + + + +github.com/ditrit/shoset/test/example.SimpleExample->github.com/ditrit/shoset.NewShoset + + + + + + + + +github.com/ditrit/shoset/test/example.SimpleExample->(*github.com/ditrit/shoset.Shoset).InitPKI + + + + + + + + +github.com/ditrit/shoset/test/example.SimpleExample->(*github.com/ditrit/shoset.Shoset).Protocol + + + + + + + + +github.com/ditrit/shoset/test/example.SimpleExample->(*github.com/ditrit/shoset.Shoset).Send + + + + + + + + +github.com/ditrit/shoset/test/example.SimpleExample->(*github.com/ditrit/shoset.Shoset).Wait + + + + + + + + +github.com/ditrit/shoset/test/example.SimpleExample->(*github.com/ditrit/shoset.Shoset).WaitForProtocols + + + + + + + + +github.com/ditrit/shoset/test/old_test.TestJoin1 + + +TestJoin1 + + + + + +github.com/ditrit/shoset/test/old_test.TestJoin1->github.com/ditrit/shoset.NewShoset + + + + + + + + +github.com/ditrit/shoset/test/old_test.TestJoin1->(*github.com/ditrit/shoset.Shoset).Bind + + + + + + + + +github.com/ditrit/shoset/test/old_test.TestJoin1->(*github.com/ditrit/shoset.Shoset).Protocol + + + + + + + + +github.com/ditrit/shoset/test/utils_for_test.CreateShosetOnlyBindFromTopology + + +CreateShosetOnlyBindFromTopology + + + + + +github.com/ditrit/shoset/test/utils_for_test.CreateShosetOnlyBindFromTopology->github.com/ditrit/shoset.NewShoset + + + + + + + + +github.com/ditrit/shoset/test/utils_for_test.CreateShosetOnlyBindFromTopology->(*github.com/ditrit/shoset.Shoset).Protocol + + + + + + + + +github.com/ditrit/shoset/test/utils_for_test.CreateShosetFromTopology + + +CreateShosetFromTopology + + + + + +github.com/ditrit/shoset/test/utils_for_test.CreateShosetFromTopology->github.com/ditrit/shoset.NewShoset + + + + + + + + +github.com/ditrit/shoset/test/utils_for_test.CreateShosetFromTopology->(*github.com/ditrit/shoset.Shoset).InitPKI + + + + + + + + +github.com/ditrit/shoset/test/utils_for_test.CreateShosetFromTopology->(*github.com/ditrit/shoset.Shoset).Protocol + + + + + + + + +github.com/ditrit/shoset/test/utils_for_test.CreateManyShosets + + +CreateManyShosets + + + + + +github.com/ditrit/shoset/test/utils_for_test.CreateManyShosets->github.com/ditrit/shoset.NewShoset + + + + + + + + +github.com/ditrit/shoset/test/utils_for_test.CreateManyShosets->(*github.com/ditrit/shoset.Shoset).InitPKI + + + + + + + + +github.com/ditrit/shoset/test/utils_for_test.CreateManyShosets->(*github.com/ditrit/shoset.Shoset).Protocol + + + + + + + + +github.com/ditrit/shoset/test/utils_for_test.WaitForManyShosets + + +WaitForManyShosets + + + + + +github.com/ditrit/shoset/test/utils_for_test.WaitForManyShosets->(*github.com/ditrit/shoset.Shoset).WaitForProtocols + + + + + + + + diff --git a/uml/class_diagram_shoset.svg b/uml/class_diagram_shoset.svg new file mode 100644 index 0000000..b62949c --- /dev/null +++ b/uml/class_diagram_shoset.svg @@ -0,0 +1,2418 @@ +Shoset class diagramconcurentDataevent_bussynceventBusmainmsgrsax509listbufiogobshosetviperconcurent_datanettlszerologutilsForTestConcurentSlicesliceValues []stringeventBus event_bus.EventBusm sync.MutexsliceValues []stringeventBus event_bus.EventBusm sync.RWMutexsliceValues []stringeventBus event_bus.EventBusm sync.RWMutexsliceValues []stringeventBus event_bus.EventBusm sync.RWMutexsliceValues []stringeventBus event_bus.EventBusm sync.RWMutexAppendToConcurentSlice(data string)DeleteFromConcurentSlice(data string)WaitForEmpty(timeout int) errorWaitForChange(timeout int) errorString() stringAppendToConcurentSlice(data string)DeleteFromConcurentSlice(data string)WaitForEmpty(timeout int) errorWaitForChange(timeout int) errorString() stringAppendToConcurentSlice(data string)DeleteFromConcurentSlice(data string)WaitForEmpty(timeout int) errorWaitForChange(timeout int) errorString() stringAppendToConcurentSlice(data string)DeleteFromConcurentSlice(data string)WaitForEmpty(timeout int) errorWaitForChange(timeout int) errorContains(data string) boolString() stringAppendToConcurentSlice(data string)DeleteFromConcurentSlice(data string)WaitForEmpty(timeout int) errorWaitForChange(timeout int) errorContains(data string) boolString() stringEventBusMutexRWMutexMapChannelTypeEventBussubscribersmap[string]DataChannelSlicem sync.Mutexsubscribersmap[string]DataChannelSlicem sync.RWMutexsubscribersmap[string]DataChannelSlicem sync.RWMutexsubscribersmap[string]DataChannelSlicem sync.RWMutexsubscribersmap[string]DataChannelSlicem sync.RWMutexsubscribersmap[string]DataChannelSlicem sync.RWMutexPublish(topic string, datainterface{})Subscribe(topic string, ch DataChannel)UnSubscribe(topic string, ch DataChannel) errorPublish(topic string, datainterface{})Subscribe(topic string, ch DataChannel)UnSubscribe(topic string, ch DataChannel) errorPublish(topic string, datainterface{})Subscribe(topic string, ch DataChannel)UnSubscribe(topic string, ch DataChannel) errorPublish(topic string, datainterface{})Subscribe(topic string, ch DataChannel)UnSubscribe(topic string, ch DataChannel) errorPublish(topic string, datainterface{})Subscribe(topic string, ch DataChannel)UnSubscribe(topic string, ch DataChannel)Publish(topic string, datainterface{})Subscribe(topic string, ch DataChannel)UnSubscribe(topic string, ch DataChannel) errorDataChannelDataChannelSlicechan interface{}[]DataChannelShosetCreationlname stringstype stringsrc stringdst []stringptype stringlaunched boolCellkey stringtimeout int64m Messagekey stringtimeout int64m Messagekey stringtimeout int64m Messagekey stringtimeout int64m Messagekey stringtimeout int64m Messagekey stringtimeout int64m Message RemoteShosetType stringRemoteAddress stringRemoteShosetType stringRemoteAddress stringRemoteShosetType stringRemoteAddress stringRemoteShosetType stringRemoteAddress stringRemoteShosetType stringRemoteAddress stringRemoteShosetType stringRemoteAddress stringGetMessage() MessageGetMessage() MessageGetMessage() MessageGetMessage() MessageGetMessage() MessageGetMessage() MessageCommandTarget stringCommand stringContextmap[string]interface{}Target stringCommand stringContextmap[string]interface{}Target stringCommand stringContextmap[string]interface{}Target stringCommand stringContextmap[string]interface{}Target stringCommand stringContextmap[string]interface{}Target stringCommand stringContextmap[string]interface{}GetMsgType() stringGetTarget() stringGetCommand() stringGetContext()map[string]interface{}GetMessageType() stringGetTarget() stringGetCommand() stringGetContext()map[string]interface{}GetMessageType() stringGetTarget() stringGetCommand() stringGetContext()map[string]interface{}GetMessageType() stringGetTarget() stringGetCommand() stringGetContext()map[string]interface{}GetMessageType() stringGetTarget() stringGetCommand() stringGetContext()map[string]interface{}GetMessageType() stringGetTarget() stringGetCommand() stringGetContext()map[string]interface{}ConfigTarget stringCommand stringContextmap[string]interface{}Target stringCommand stringContextmap[string]interface{}Target stringCommand stringContextmap[string]interface{}Target stringCommand stringContextmap[string]interface{}Target stringCommand stringContextmap[string]interface{}Target stringCommand stringContextmap[string]interface{}GetMsgType() stringGetTarget() stringGetCommand() stringGetContext()map[string]interface{}GetMessageType() stringGetTarget() stringGetCommand() stringGetContext()map[string]interface{}GetMessageType() stringGetTarget() stringGetCommand() stringGetContext()map[string]interface{}GetMessageType() stringGetTarget() stringGetCommand() stringGetContext()map[string]interface{}GetMessageType() stringGetTarget() stringGetCommand() stringGetContext()map[string]interface{}GetMessageType() stringGetTarget() stringGetCommand() stringGetContext()map[string]interface{}ConfigProtocolCommandName stringLogicalName stringShosetType stringAddress stringMyBrothers []stringYourBrothers []stringCommandName stringLogicalName stringShosetType stringBindAddress stringMyBrothers []stringYourBrothers []stringCommandName stringLogicalName stringShosetType stringBindAddress stringMyBrothers []stringYourBrothers []stringCommandName stringLogicalName stringShosetType stringBindAddress stringMyBrothers []stringYourBrothers []stringCommandName stringLogicalName stringShosetType stringBindAddress stringMyBrothers []stringYourBrothers []stringCommandName stringLogicalName stringShosetType stringBindAddress stringMyBrothers []stringYourBrothers []stringGetMsgType() stringGetLogicalName() stringGetShosetType() stringGetAddress() stringGetCommandName() stringGetMyBrothers() []stringGetYourBrothers() []stringGetMessageType() stringGetLogicalName() stringGetShosetType() stringGetAddress() stringGetCommandName() stringGetMyBrothers() []stringGetYourBrothers() []stringGetMessageType() stringGetLogicalName() stringGetShosetType() stringGetAddress() stringGetCommandName() stringGetMyBrothers() []stringGetYourBrothers() []stringGetMessageType() stringGetLogicalName() stringGetShosetType() stringGetAddress() stringGetCommandName() stringGetMyBrothers() []stringGetYourBrothers() []stringGetMessageType() stringGetLogicalName() stringGetShosetType() stringGetAddress() stringGetCommandName() stringGetMyBrothers() []stringGetYourBrothers() []stringGetMessageType() stringGetLogicalName() stringGetShosetType() stringGetAddress() stringGetCommandName() stringGetMyBrothers() []stringGetYourBrothers() []stringEventTopic stringEvent stringReferenceUUID stringTopic stringEvent stringReferenceUUID stringTopic stringEvent stringReferenceUUID stringTopic stringEvent stringReferenceUUID stringTopic stringEvent stringReferenceUUID stringTopic stringEvent stringReferenceUUID stringGetMsgType() stringGetTopic() stringGetEvent() stringGetReferenceUUID() stringGetMessageType() stringGetTopic() stringGetEvent() stringGetReferenceUUID() stringGetMessageType() stringGetTopic() stringGetEvent() stringGetReferenceUUID() stringGetMessageType() stringGetTopic() stringGetEvent() stringGetReferenceUUID() stringGetMessageType() stringGetTopic() stringGetEvent() stringGetReferenceUUID() stringGetMessageType() stringGetTopic() stringGetEvent() stringGetReferenceUUID() stringForwardAckOGMessageUUID stringOGMessageTimeStamp int64OGMessageUUID stringOGMessageTimeStamp int64OGMessageUUID stringOGMessageTimeStamp int64OGMessageUUID stringOGMessageTimeStamp int64OGMessageUUID stringOGMessageTimeStamp int64GetMessageType() stringGetOGMessageUUID() stringGetOGMessageTimeStamp() int64GetMessageType() stringGetOGMessageUUID() stringGetOGMessageTimeStamp() int64GetMessageType() stringGetOGMessageUUID() stringGetOGMessageTimeStamp() int64GetMessageType() stringGetOGMessageUUID() stringGetOGMessageTimeStamp() int64GetMessageType() stringGetOGMessageUUID() stringGetOGMessageTimeStamp() int64Iteratorqueue *Queuecurrent stringm sync.Mutexqueue *Queuecurrent stringm sync.Mutexqueue *Queuecurrent stringm sync.Mutexqueue *Queuecurrent stringm sync.Mutexqueue *Queuecurrent stringm sync.Mutexqueue *Queuecurrent stringm sync.MutexInit(queue *Queue)Close()Get() *CellPrintQueue()Init(queue *Queue)Close()Get() *CellPrintQueue()GetQueue() *QueueInit(queue *Queue)Close()Get() *CellPrintQueue()GetQueue() *QueueInit(queue *Queue)Close()Get() *CellPrintQueue()GetQueue() *QueueInit(queue *Queue)Close()Get() *CellPrintQueue()GetQueue() *QueueInit(queue *Queue)Close()Get() *CellPrintQueue()GetQueue() *QueueMessageGetMsgType() stringGetUUID() stringGetTenant() stringGetToken() stringGetTimestamp() int64GetTimeout() int64GetPayload() stringGetMajor() int8GetMinor() int8GetMessageType() stringGetUUID() stringGetTenant() stringGetToken() stringGetTimestamp() int64GetTimeout() int64GetPayload() stringGetMajor() int8GetMinor() int8GetDestinationLname() stringGetMessageType() stringGetUUID() stringGetTenant() stringGetToken() stringGetTimestamp() int64GetTimeout() int64GetPayload() stringGetMajor() int8GetMinor() int8GetDestinationLname() stringGetMessageType() stringGetUUID() stringGetTenant() stringGetToken() stringGetTimestamp() int64GetTimeout() int64GetPayload() stringGetMajor() int8GetMinor() int8GetDestinationLname() stringGetMessageType() stringGetUUID() stringGetTenant() stringGetToken() stringGetTimestamp() int64GetTimeout() int64GetPayload() stringGetMajor() int8GetMinor() int8GetDestinationLname() stringGetMessageType() stringGetUUID() stringGetTenant() stringGetToken() stringGetTimestamp() int64GetTimeout() int64GetPayload() stringGetMajor() int8GetMinor() int8GetDestinationLname() stringMessageBaseUUID stringTenant stringToken stringTimeout int64Timestamp int64Payload stringNext stringMajor int8Minor int8UUID stringTenant stringToken stringTimeout int64Timestamp int64Payload stringNext stringMajor int8Minor int8DestinationLname stringUUID stringTenant stringToken stringTimeout int64Timestamp int64Payload stringNext stringMajor int8Minor int8DestinationLname stringUUID stringTenant stringToken stringTimeout int64Timestamp int64Payload stringNext stringMajor int8Minor int8DestinationLname stringUUID stringTenant stringToken stringTimeout int64Timestamp int64Payload stringNext stringMajor int8Minor int8DestinationLname stringUUID stringTenant stringToken stringTimeout int64Timestamp int64Payload stringNext stringMajor int8Minor int8DestinationLname stringInitMessageBase()GetUUID() stringSetUUID(newUUID string)GetTenant() stringGetToken() stringGetTimestamp() int64GetTimeout() int64GetPayload() stringGetMajor() int8GetMinor() int8InitMessageBase()GetUUID() stringSetUUID(newUUID string)GetTenant() stringGetToken() stringGetTimestamp() int64GetTimeout() int64GetPayload() stringGetMajor() int8GetMinor() int8GetDestinationLname() stringSetDestinationLname(s string)InitMessageBase()GetUUID() stringSetUUID(newUUID string)GetTenant() stringGetToken() stringGetTimestamp() int64GetTimeout() int64GetPayload() stringGetMajor() int8GetMinor() int8GetDestinationLname() stringSetDestinationLname(s string)InitMessageBase()GetUUID() stringSetUUID(newUUID string)GetTenant() stringGetToken() stringGetTimestamp() int64GetTimeout() int64GetPayload() stringGetMajor() int8GetMinor() int8GetDestinationLname() stringSetDestinationLname(s string)InitMessageBase()GetUUID() stringSetUUID(newUUID string)GetTenant() stringGetToken() stringGetTimestamp() int64GetTimeout() int64GetPayload() stringGetMajor() int8GetMinor() int8GetDestinationLname() stringSetDestinationLname(s string)InitMessageBase()GetUUID() stringSetUUID(newUUID string)GetTenant() stringGetToken() stringGetTimestamp() int64GetTimeout() int64GetPayload() stringGetMajor() int8GetMinor() int8GetDestinationLname() stringSetDestinationLname(s string)PkiEventRequestAddress stringCommand stringSecret stringLogicalName stringCertReq *x509.CertificateSignedCert []byteHostPublicKey *rsa.PublicKeyCAprivateKey *rsa.PrivateKeyCAcert []byteRequestAddress stringCommand stringSecret stringLogicalName stringCertificateRequest *x509.CertificateSignedCertificate []byteHostPublicKey *rsa.PublicKeyCAprivateKey *rsa.PrivateKeyCAcertificate []byteRequestAddress stringCommand stringSecret stringLogicalName stringCertificateRequest *x509.CertificateSignedCertificate []byteHostPublicKey *rsa.PublicKeyCAprivateKey *rsa.PrivateKeyCAcertificate []byteRequestAddress stringCommand stringSecret stringLogicalName stringCertificateRequest *x509.CertificateSignedCertificate []byteHostPublicKey *rsa.PublicKeyCAprivateKey *rsa.PrivateKeyCAcertificate []byteRequestAddress stringCommand stringSecret stringLogicalName stringCertificateRequest *x509.CertificateSignedCertificate []byteHostPublicKey *rsa.PublicKeyCAprivateKey *rsa.PrivateKeyCAcertificate []byteRequestAddress stringCommand stringSecret stringLogicalName stringCertificateRequest *x509.CertificateSignedCertificate []byteHostPublicKey *rsa.PublicKeyCAprivateKey *rsa.PrivateKeyCAcertificate []byteGetMsgType() stringGetSecret() stringGetCommand() stringSetCommand(command string)GetRequestAddress() stringGetLogicalName() stringGetCertReq() *x509.CertificateGetSignedCert() []byteGetHostPublicKey() *rsa.PublicKeyGetCAprivateKey() *rsa.PrivateKeyGetCAcert() []byteGetMessageType() stringGetSecret() stringGetCommand() stringSetCommand(command string)GetRequestAddress() stringGetLogicalName() stringGetCertificateRequest() *x509.CertificateGetSignedCert() []byteGetHostPublicKey() *rsa.PublicKeyGetCAprivateKey() *rsa.PrivateKeyGetCAcert() []byteGetMessageType() stringGetSecret() stringGetCommand() stringSetCommand(command string)GetRequestAddress() stringGetLogicalName() stringGetCertificateRequest() *x509.CertificateGetSignedCert() []byteGetHostPublicKey() *rsa.PublicKeyGetCAprivateKey() *rsa.PrivateKeyGetCAcert() []byteGetMessageType() stringGetSecret() stringGetCommand() stringSetCommand(command string)GetRequestAddress() stringGetLogicalName() stringGetCertificateRequest() *x509.CertificateGetSignedCert() []byteGetHostPublicKey() *rsa.PublicKeyGetCAprivateKey() *rsa.PrivateKeyGetCAcert() []byteGetMessageType() stringGetSecret() stringGetCommand() stringSetCommand(command string)GetRequestAddress() stringGetLogicalName() stringGetCertificateRequest() *x509.CertificateGetSignedCert() []byteGetHostPublicKey() *rsa.PublicKeyGetCAprivateKey() *rsa.PrivateKeyGetCAcert() []byteGetMessageType() stringGetSecret() stringGetCommand() stringSetCommand(command string)GetRequestAddress() stringGetLogicalName() stringGetCertificateRequest() *x509.CertificateGetSignedCert() []byteGetHostPublicKey() *rsa.PublicKeyGetCAprivateKey() *rsa.PrivateKeyGetCAcert() []byteQueueqlist list.Listdictmap[string]*list.Elementitersmap[*Iterator]boolm sync.Mutexqlist list.Listdictmap[string]*list.Elementitersmap[*Iterator]boolm sync.Mutexqlist list.Listdictmap[string]*list.Elementitersmap[*Iterator]boolm sync.Mutexqlist list.Listdictmap[string]*list.Elementitersmap[*Iterator]boolm sync.Mutexqlist list.Listdictmap[string]*list.Elementitersmap[*Iterator]boolm sync.Mutexqlist list.Listdictmap[string]*list.Elementitersmap[*Iterator]boolm sync.Mutexremove(key string)remove(key string)remove(key string)remove(key string)remove(key string)remove(key string) Init()GetByReferencesUUID(uuid string) *EventPush(m Message, RemoteShosetType string, RemoteAddress string) boolFirst() *CellNext(key string) *CellIsEmpty() boolPrint()Init()GetByReferencesUUID(uuid string) *EventPush(m Message, RemoteShosetType string, RemoteAddress string) boolFirst() *CellNext(key string) *CellIsEmpty() boolPrint()LockQueue()UnlockQueue()Init()GetByReferencesUUID(uuid string) *EventPush(m Message, RemoteShosetType string, RemoteAddress string) boolFirst() *CellNext(key string) *CellIsEmpty() boolPrint()LockQueue()UnlockQueue()Init()GetByReferencesUUID(uuid string) *EventPush(m Message, RemoteShosetType string, RemoteAddress string) boolFirst() *CellNext(key string) *CellIsEmpty() boolPrint()LockQueue()UnlockQueue()Init()GetByReferencesUUID(uuid string) *EventPush(m Message, RemoteShosetType string, RemoteAddress string) boolFirst() *CellNext(key string) *CellIsEmpty() boolPrint()LockQueue()UnlockQueue()Init()GetByReferencesUUID(uuid string) *EventPush(m Message, RemoteShosetType string, RemoteAddress string) boolFirst() *CellNext(key string) *CellIsEmpty() boolPrint()LockQueue()UnlockQueue()Readerb *bufio.Readerdec *gob.Decoderm sync.Mutexb *bufio.Readerdec *gob.Decoderm sync.Mutexb *bufio.Readerdec *gob.Decoderm sync.Mutexb *bufio.Readerdec *gob.Decoderm sync.Mutexb *bufio.Readerdec *gob.Decoderm sync.Mutexb *bufio.Readerdec *gob.Decoderm sync.MutexUpdateReader(rd io.Reader)ReadString() (string, error)ReadMessage(datainterface{}) errorUpdateReader(rd io.Reader)ReadString() (string, error)ReadMessage(datainterface{}) errorUpdateReader(rd io.Reader)ReadString() (string, error)ReadMessage(datainterface{}) errorUpdateReader(rd io.Reader)ReadString() (string, error)ReadMessage(datainterface{}) errorUpdateReader(rd io.Reader)ReadString() (string, error)ReadMessage(datainterface{}) errorUpdateReader(rd io.Reader)ReadString() (string, error)ReadMessage(datainterface{}) errorRoutingEventOrigin stringNbSteps intOrigin stringNbSteps intOrigin stringNbSteps intRerouteTimestamp int64Origin stringNbSteps intRerouteTimestamp int64Origin stringNbSteps intRerouteTimestamp int64Origin stringNbSteps intRerouteTimestamp int64GetMsgType() stringGetOrigin() stringGetNbSteps() intSetNbSteps(i int)GetMessageType() stringGetOrigin() stringGetNbSteps() intSetNbSteps(i int)GetMessageType() stringGetOrigin() stringGetNbSteps() intSetNbSteps(i int)GetRerouteTimestamp() int64SetRerouteTimestamp(i int64)GetMessageType() stringGetOrigin() stringGetNbSteps() intSetNbSteps(i int)GetRerouteTimestamp() int64SetRerouteTimestamp(i int64)GetMessageType() stringGetOrigin() stringGetNbSteps() intSetNbSteps(i int)GetRerouteTimestamp() int64SetRerouteTimestamp(i int64)GetMessageType() stringGetOrigin() stringGetNbSteps() intSetNbSteps(i int)GetRerouteTimestamp() int64SetRerouteTimestamp(i int64)SimpleMessageGetMessageType() stringGetMessageType() stringGetMessageType() stringGetMessageType() stringGetMessageType() stringWriterb *bufio.Writerenc *gob.Encoderm sync.Mutexb *bufio.Writerenc *gob.Encoderm sync.Mutexb *bufio.Writerenc *gob.Encoderm sync.Mutexb *bufio.Writerenc *gob.Encoderm sync.Mutexb *bufio.Writerenc *gob.Encoderm sync.Mutexb *bufio.Writerenc *gob.Encoderm sync.MutexGetBufioWriter() *bufio.WriterUpdateWriter(wd io.Writer)Flush() errorWriteString(data string) (int, error)WriteMessage(datainterface{}) errorUpdateWriter(wd io.Writer)Flush() errorSendMessage(msg Message) errorWriteString(data string) (int, error)WriteMessage(datainterface{}) errorUpdateWriter(wd io.Writer)Flush() errorSendMessage(msg Message) errorWriteString(data string) (int, error)WriteMessage(datainterface{}) errorUpdateWriter(wd io.Writer)Flush() errorSendMessage(msg Message) errorWriteString(data string) (int, error)WriteMessage(datainterface{}) errorUpdateWriter(wd io.Writer)Flush() errorSendMessage(msg Message) errorWriteString(data string) (int, error)WriteMessage(datainterface{}) errorUpdateWriter(wd io.Writer)Flush() errorSendMessage(msg Message) errorWriteString(data string) (int, error)WriteMessage(datainterface{}) errorPrivateKeyPublicKeyCertificateElementListReaderWriterDecoderEncoderCommandHandlerGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageConfigbaseDir stringfileName stringviper *viper.Vipermu sync.MutexbaseDir stringfileName stringviper *viper.Vipermu sync.MutexbaseDir stringfileName stringviper *viper.Vipermu sync.MutexbaseDirectory stringfileName stringviper *viper.Vipermu sync.MutexbaseDirectory stringfileName stringviper *viper.Vipermu sync.MutexbaseDirectory stringfileName stringviper *viper.Vipermu sync.MutexGetBaseDir() stringGetFileName() stringSetFileName(fileName string)InitFolders(name string) (string, error)ReadConfig(fileName string) errorWriteConfig(fileName string) errorSet(key string, valueinterface{})GetSlice(protocol string) []stringGetBaseDir() stringGetFileName() stringSetFileName(fileName string)InitFolders(name string) (string, error)ReadConfig(fileName string) errorWriteConfig(fileName string) errorSet(key string, valueinterface{})GetSlice(protocol string) []stringGetBaseDir() stringGetFileName() stringSetFileName(fileName string)InitFolders(name string) (string, error)ReadConfig(fileName string) errorWriteConfig(fileName string) errorAppendToKey(key string, values []string)DeleteFromKey(key string, value []string)GetSlice(protocol string) []stringGetBaseDirectory() stringGetFileName() stringSetFileName(fileName string)InitFolders(name string) (string, error)ReadConfig(fileName string) errorWriteConfig(fileName string) errorAppendToKey(key string, values []string)DeleteFromKey(key string, value []string)GetSlice(protocol string) []stringGetBaseDirectory() stringGetFileName() stringSetFileName(fileName string)InitFolders(name string) (string, error)ReadConfig(fileName string) errorWriteConfig(fileName string) errorAppendToKey(key string, values []string)DeleteFromKey(key string, value []string)GetSlice(protocol string) []stringGetBaseDirectory() stringGetFileName() stringSetFileName(fileName string)InitFolders(name string) (string, error)ReadConfig(fileName string) errorWriteConfig(fileName string) errorAppendToKey(key string, values []string)DeleteFromKey(key string, value []string)GetSlice(protocol string) []stringConfigByeHandlerGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageConfigHandlerGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, cmd msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, cmd msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, cmd msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, cmd msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, cmd msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, cmd msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageConfigJoinHandlerGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageConfigLinkHandlerGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageEventHandlerGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageForwardAcknownledgeHandlerGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageMapSyncMapsmap sync.Mapcfg *Configcfg *Configcfg *Configcfg *Configcfg *Configcfg *ConfigprepareUpdateFile(protocol string, ipAddresses []string)updateFile(protocol string, keys []string)prepareUpdateFile(protocol string, ipAddresses []string)updateFile(protocol string, keys []string)updateFile(protocol string, keys []string)updateFile(protocol string, addresses []string) errorupdateFile(protocol string, addresses []string) errorupdateFile(protocol string, addresses []string) error GetConfig() *ConfigSetConfig(cfg *Config)Store(lName string, key string, protocol string, valueinterface{})Delete(lName string, connIpAddress string)Iterate(iterfunc(string,interface{}) )Keys(sType string) []stringGetConfig() *ConfigSetConfig(cfg *Config)StoreConfig(lName string, key string, protocol string, valueinterface{})DeleteConfig(lName string, connIpAddress string)Iterate(iterfunc(string,interface{}) )Keys(sType string) []stringAppendToKey(key1 string, key2 string, valueinterface{})String() stringGetConfig() *ConfigSetConfig(cfg *Config)StoreConfig(lName string, key string, protocol string, valueinterface{})Iterate(iterfunc(string,interface{}) )Keys(sType string) []stringAppendToKeys(key1 string, key2 string, valueinterface{})String() stringLoadValueFromKeys(key1 string, key2 string) (interface{}, bool)DeleteValueFromKeys(key1 string, key2 string)GetConfig() *ConfigSetConfig(cfg *Config)StoreConfig(lName string, address string, protocol string, conninterface{}) errorIterate(iterfunc(string,interface{}) )Keys(sType string) []stringAppendToKeys(key1 string, key2 string, valueinterface{})String() stringLoadValueFromKeys(key1 string, key2 string) (interface{}, bool)DeleteValueFromKeys(key1 string, key2 string)GetConfig() *ConfigSetConfig(cfg *Config)StoreConfig(lName string, address string, protocol string, conninterface{}) errorIterate(iterfunc(string,interface{}) )Keys(sType string) []stringAppendToKeys(key1 string, key2 string, valueinterface{})String() stringLoadValueFromKeys(key1 string, key2 string) (interface{}, bool)DeleteValueFromKeys(key1 string, key2 string)GetConfig() *ConfigSetConfig(cfg *Config)StoreConfig(lName string, address string, protocol string, conninterface{}) errorIterate(iterfunc(string,interface{}) )Keys(sType string) []stringAppendToKeys(key1 string, key2 string, valueinterface{})String() stringLoadValueFromKeys(key1 string, key2 string) (interface{}, bool)DeleteValueFromKeys(key1 string, key2 string)MessageHandlersGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessagePkiEventHandlerGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageProtectedStatusvalue boolm sync.Mutexvalue boolm sync.RWMutexRouteneighbour stringnb_steps intuuid stringneighbour stringneighborConn *ShosetConnnb_steps intuuid stringtimestamp int64neighbour stringneighborConn *ShosetConnnb_steps intuuid stringtimestamp int64neighbour stringneighborConn *ShosetConnnb_steps intuuid stringtimestamp int64neighbour stringneighborConn *ShosetConnnb_steps intuuid stringtimestamp int64neighbour stringneighborConn *ShosetConnnb_steps intuuid stringtimestamp int64GetNeighbour() stringGetNbSteps() intGetUUID() stringSetNeighbour(s string)SetNbSteps(i int)SetUUID(s string)GetNeighbour() stringGetNeighborConn() *ShosetConnGetNbSteps() intGetUUID() stringSetNeighbour(s string)SetNbSteps(i int)SetUUID(s string)GetNeighbour() stringGetNeighborConn() *ShosetConnGetNbSteps() intGetUUID() stringGetTimestamp() int64SetNeighbour(s string)SetNeighborConn(c *ShosetConn)SetNbSteps(i int)GetNeighbour() stringGetNeighborConn() *ShosetConnGetNbSteps() intGetUUID() stringGetTimestamp() int64SetNeighbour(s string)SetNeighborConn(c *ShosetConn)SetNbSteps(i int)GetNeighbour() stringGetNeighborConn() *ShosetConnGetNbSteps() intGetUUID() stringGetTimestamp() int64SetNeighbour(s string)SetNeighborConn(c *ShosetConn)SetNbSteps(i int)GetNeighbour() stringGetNeighborConn() *ShosetConnGetNbSteps() intGetUUID() stringGetTimestamp() int64SetNeighbour(s string)SetNeighborConn(c *ShosetConn)SetNbSteps(i int)RoutingEventHandlerGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, evt msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageShosetbindAddress stringlogicalName stringshosetType stringisValid boolisPki boollistener net.ListenertlsConfigSingleWay *tls.ConfigtlsConfigDoubleWay *tls.ConfigbindAddress stringlogicalName stringshosetType stringisValid boolisPki boollistener net.ListenertlsConfigSingleWay *tls.ConfigtlsConfigDoubleWay *tls.ConfigbindAddress stringlogicalName stringshosetType stringisValid boolisPki boollistener net.ListenertlsConfigSingleWay *tls.ConfigtlsConfigDoubleWay *tls.ConfigbindAddress stringlogicalName stringshosetType stringisValid boolisPki boollistener net.ListenertlsConfigSingleWay *tls.ConfigtlsConfigDoubleWay *tls.Configmu sync.RWMutexbindAddress stringlogicalName stringshosetType stringisValid boolisPki boollistener net.ListenertlsConfigSingleWay *tls.ConfigtlsConfigDoubleWay *tls.Configmu sync.RWMutexbindAddress stringlogicalName stringshosetType stringisValid boolisPki boollistener net.ListenertlsConfigSingleWay *tls.ConfigtlsConfigDoubleWay *tls.Configmu sync.RWMutex Logger zerolog.LoggerContextmap[string]interface{}ConnsByLname *MapSyncMapLnamesByType *MapSyncMapLnamesByProtocol *MapSyncMapConnsSingleBool *sync.MapConnsSingleConn *sync.MapRouteTable *sync.MapQueuemap[string]*msg.QueueHandlersmap[string]MessageHandlersDonechanboolLogger zerolog.LoggerContextmap[string]interface{}ConnsByLname *MapSyncMapLnamesByType *MapSyncMapLnamesByProtocol *MapSyncMapConnsSingleBool *sync.MapConnsSingleConn *sync.MapRouteTable *sync.MapRoutingEventBus event_bus.EventBusQueuemap[string]*msg.QueueHandlersmap[string]MessageHandlersMessageEventBus event_bus.EventBusDonechanboolLaunchedProtocol concurent_data.ConcurentSliceLogger zerolog.LoggerContextmap[string]interface{}ConnsByLname *MapSyncMapLnamesByType *MapSyncMapLnamesByProtocol *MapSyncMapConnsSingleBool *sync.MapConnsSingleConn *sync.MapRouteTable *sync.MapRoutingEventBus event_bus.EventBusQueuemap[string]*msg.QueueHandlersmap[string]MessageHandlersMessageEventBus event_bus.EventBusDonechanboolLaunchedProtocol concurent_data.ConcurentSliceLogger zerolog.LoggerContextmap[string]interface{}ConnsByLname *MapSyncMapLnamesByType *MapSyncMapLnamesByProtocol *MapSyncMapConnsSingleBool *sync.MapConnsSingleConn *sync.MapRouteTable *sync.MapRoutingEventBus event_bus.EventBusQueuemap[string]*msg.QueueHandlersmap[string]MessageHandlersMessageEventBus event_bus.EventBusDonechanboolLaunchedProtocol concurent_data.ConcurentSliceLogger zerolog.LoggerContextmap[string]interface{}ConnsByLname *MapSyncMapLnamesByType *MapSyncMapLnamesByProtocol *MapSyncMapConnsSingleBool *sync.MapConnsSingleConn *sync.MapRouteTable *sync.MapRoutingEventBus event_bus.EventBusQueuemap[string]*msg.QueueHandlersmap[string]MessageHandlersMessageEventBus event_bus.EventBusDonechanboolLaunchedProtocol concurent_data.ConcurentSliceLogger zerolog.LoggerContextmap[string]interface{}ConnsByLname *MapSyncMapLnamesByType *MapSyncMapLnamesByProtocol *MapSyncMapConnsSingleBool *sync.MapConnsSingleConn *sync.MapRouteTable *sync.MapRoutingEventBus event_bus.EventBusQueuemap[string]*msg.QueueHandlersmap[string]MessageHandlersMessageEventBus event_bus.EventBusDonechanboolLaunchedProtocol concurent_data.ConcurentSlicedeleteConn(connAddr string, connLname string)handleBind()deleteConn(connAddr string, connLname string)handleBind()forwardMessage(m msg.Message)deleteConn(Lname string, remoteAddress string)handleBind()forwardMessage(m msg.Message)initCA(formattedIpAddress string)initShoset(bindAddress string)handleBind()forwardMessage(m msg.Message)initCA(formattedIpAddress string)initShoset(bindAddress string)handleBind()forwardMessage(m msg.Message)initCA(formattedIpAddress string)initShoset(bindAddress string)handleBind()forwardMessage(m msg.Message) InitPKI(bindAddress string)InitCA(bindAddress string)InitShoset(bindAddress string)GenerateSecret(login string, password string) stringSignCertificate(certReq *x509.Certificate, hostPublicKey *rsa.PublicKey) []byteSetUpDoubleWay() errorCertify(bindAddress string, remoteAddress string) errorGetBindAddress() stringGetLogicalName() stringGetShosetType() stringGetIsValid() boolGetIsPki() boolGetListener() net.ListenerGetTlsConfigSingleWay() *tls.ConfigGetTlsConfigDoubleWay() *tls.ConfigGetConnsByTypeArray(shosetType string) []*ShosetConnIsCertified(path string) boolSetBindAddress(bindAddress string)SetIsValid(state bool)SetIsPki(state bool)SetListener(listener net.Listener)String() stringBind(address string) errorProtocol(bindAddress string, remoteAddress string, protocolType string)Send(msg msg.Message)Wait(msgType string, argsmap[string]string, timeout int, iterator *msg.Iterator) msg.MessageInitPKI(bindAddress string)InitCA(bindAddress string)InitShoset(bindAddress string)GenerateSecret(login string, password string) stringSignCertificate(certificateRequest *x509.Certificate, hostPublicKey *rsa.PublicKey) []byteSetUpDoubleWay() errorCertify(bindAddress string, remoteAddress string) errorGetBindAddress() stringGetLogicalName() stringGetShosetType() stringGetIsValid() boolGetIsPki() boolGetListener() net.ListenerGetTlsConfigSingleWay() *tls.ConfigGetTlsConfigDoubleWay() *tls.ConfigGetConnsByTypeArray(shosetType string) []*ShosetConnIsCertified(path string) boolSetBindAddress(bindAddress string)SetIsValid(state bool)SetIsPki(state bool)SetListener(listener net.Listener)WaitForProtocols(timeout int)String() stringBind(address string) errorProtocol(bindAddress string, remoteAddress string, protocolType string)SaveRoute(c *ShosetConn, routingEvt *msg.RoutingEvent)Send(msg msg.Message)Wait(msgType string, argsmap[string]string, timeout int, iterator *msg.Iterator) msg.MessageInitPKI(bindAddress string)InitCA(bindAddress string)InitShoset(bindAddress string)GenerateSecret(login string, password string) stringSignCertificate(certificateRequest *x509.Certificate, hostPublicKey *rsa.PublicKey) []byteSetUpDoubleWay() errorCertify(bindAddress string, remoteAddress string) errorGetBindAddress() stringGetLogicalName() stringGetShosetType() stringGetIsValid() boolGetIsPki() boolGetListener() net.ListenerGetTlsConfigSingleWay() *tls.ConfigGetTlsConfigDoubleWay() *tls.ConfigGetConnsByTypeArray(shosetType string) []*ShosetConnIsCertified(path string) boolSetBindAddress(bindAddress string)SetIsValid(state bool)SetIsPki(state bool)SetListener(listener net.Listener)WaitForProtocols(timeout int)String() stringBind(address string) errorProtocol(bindAddress string, remoteAddress string, protocolType string)EndProtocol(Lname string, remoteAddress string)SaveRoute(c *ShosetConn, routingEvt *msg.RoutingEvent)Send(msg msg.Message)Wait(msgType string, argsmap[string]string, timeout int, iterator *msg.Iterator) msg.MessageInitPKI(bindAddress string)GenerateSecret(login string, password string) stringSignCertificate(certificateRequest *x509.Certificate, hostPublicKey *rsa.PublicKey) []byteSetUpDoubleWay() errorCertify(bindAddress string, remoteAddress string) errorGetBindAddress() stringGetLogicalName() stringGetShosetType() stringGetIsValid() boolGetIsPki() boolGetListener() net.ListenerGetTlsConfigSingleWay() *tls.ConfigGetTlsConfigDoubleWay() *tls.ConfigGetConnsByTypeArray(shosetType string) []*ShosetConnIsCertified(path string) boolSetBindAddress(bindAddress string)SetIsValid(state bool)SetIsPki(state bool)SetListener(listener net.Listener)DeleteConn(Lname string, remoteAddress string)WaitForProtocols(timeout int)String() stringBind(address string) errorProtocol(bindAddress string, remoteAddress string, protocolType string)EndProtocol(Lname string, remoteAddress string)SaveRoute(c *ShosetConn, routingEvt *msg.RoutingEvent)Send(msg msg.Message)Wait(msgType string, argsmap[string]string, timeout int, iterator *msg.Iterator) msg.MessageInitPKI(bindAddress string)GenerateSecret(login string, password string) stringSignCertificate(certificateRequest *x509.Certificate, hostPublicKey *rsa.PublicKey) []byteSetUpDoubleWay() errorCertify(bindAddress string, remoteAddress string) errorGetBindAddress() stringGetLogicalName() stringGetShosetType() stringGetIsValid() boolGetIsPki() boolGetListener() net.ListenerGetTlsConfigSingleWay() *tls.ConfigGetTlsConfigDoubleWay() *tls.ConfigGetConnsByTypeArray(shosetType string) []*ShosetConnIsCertified(path string) boolSetBindAddress(bindAddress string)SetIsValid(state bool)SetIsPki(state bool)SetListener(listener net.Listener)DeleteConn(Lname string, remoteAddress string)WaitForProtocols(timeout int)String() stringBind(address string) errorProtocol(bindAddress string, remoteAddress string, protocolType string)EndProtocol(Lname string, remoteAddress string)SaveRoute(c *ShosetConn, routingEvt *msg.RoutingEvent)Send(msg msg.Message)Wait(msgType string, argsmap[string]string, timeout int, iterator *msg.Iterator) msg.MessageInitPKI(bindAddress string)GenerateSecret(login string, password string) stringSignCertificate(certificateRequest *x509.Certificate, hostPublicKey *rsa.PublicKey) []byteSetUpDoubleWay() errorCertify(bindAddress string, remoteAddress string) errorGetBindAddress() stringGetLogicalName() stringGetShosetType() stringGetIsValid() boolGetIsPki() boolGetListener() net.ListenerGetTlsConfigSingleWay() *tls.ConfigGetTlsConfigDoubleWay() *tls.ConfigGetConnsByTypeArray(shosetType string) []*ShosetConnIsCertified(path string) boolSetBindAddress(bindAddress string)SetIsValid(state bool)SetIsPki(state bool)SetListener(listener net.Listener)DeleteConn(Lname string, remoteAddress string)WaitForProtocols(timeout int)String() stringBind(address string) errorProtocol(bindAddress string, remoteAddress string, protocolType string)EndProtocol(Lname string, remoteAddress string)SaveRoute(c *ShosetConn, routingEvt *msg.RoutingEvent)Send(msg msg.Message)Wait(msgType string, argsmap[string]string, timeout int, iterator *msg.Iterator) msg.MessageShosetConnconn *tls.Connshoset *Shosetrb *msg.Readerwb *msg.WriterremoteLname stringremoteShosetType stringdir stringremoteAddress stringisValid boolconn *tls.Connshoset *Shosetrb *msg.Readerwb *msg.WriterremoteLname stringremoteShosetType stringdir stringremoteAddress stringisValid ProtectedStatusconn *tls.Connshoset *Shosetrb *msg.Readerwb *msg.WriterremoteLname stringremoteShosetType stringdir stringremoteAddress stringprotocol stringisValid ProtectedStatusconn *tls.Connshoset *Shosetrb *msg.Readerwb *msg.WriterremoteLname stringremoteShosetType stringdirection stringremoteAddress stringprotocol stringisValid boolmu sync.RWMutexconn *tls.Connshoset *Shosetrb *msg.Readerwb *msg.WriterremoteLname stringremoteShosetType stringdirection stringremoteAddress stringprotocol stringisValid boolmu sync.RWMutexconn *tls.Connshoset *Shosetrb *msg.Readerwb *msg.WriterremoteLname stringremoteShosetType stringdirection stringremoteAddress stringprotocol stringisValid boolmu sync.RWMutex Logger zerolog.LoggerLogger zerolog.LoggerStatusChangechanboolLogger zerolog.LoggerLogger zerolog.LoggerLogger zerolog.LoggerLogger zerolog.LoggerhandleMessageType(messageType string) errorhandleMessageType(messageType string) errorhandleMessageType(messageType string) errorhandleMessageType(messageType string) errorhandleMessageType(messageType string) errorhandleMessageType(messageType string) error RunPkiRequest(address string) errorHandleSingleWay(messageValue msg.Message) errorHandlePkiRequest(messageValue msg.Message) errorHandlePkiResponse(messageValue msg.Message) errorRunPkiRequest(address string) errorHandleSingleWay(messageValue msg.Message) errorHandlePkiRequest(messageValue msg.Message) errorHandlePkiResponse(messageValue msg.Message) errorRunPkiRequest(address string) errorHandleSingleWay(messageValue msg.Message) errorHandlePkiRequest(messageValue msg.Message) errorHandlePkiResponse(messageValue msg.Message) errorGetConn() *tls.ConnGetShoset() *ShosetGetReader() *msg.ReaderGetWriter() *msg.WriterGetRemoteLogicalName() stringGetLocalLogicalName() stringGetRemoteShosetType() stringGetLocalShosetType() stringGetDir() stringGetRemoteAddress() stringGetLocalAddress() stringGetIsValid() boolSetConn(conn *tls.Conn)UpdateConn(conn *tls.Conn)SetRemoteLogicalName(lName string)SetLocalAddress(bindAddress string)SetRemoteShosetType(ShosetType string)SetIsValid(state bool)SetRemoteAddress(address string)Store(protocol string, lName string, address string, shosetType string)String() stringHandleConfig(cfg *msg.ConfigProtocol)RunInConnSingle(address string)RunInConnDouble()SendMessage(msg msg.Message) errorReceiveMessage() errorRunPkiRequest(address string) errorHandleSingleWay(messageValue msg.Message) errorHandlePkiRequest(messageValue msg.Message) errorHandlePkiResponse(messageValue msg.Message) errorGetConn() *tls.ConnGetShoset() *ShosetGetReader() *msg.ReaderGetWriter() *msg.WriterGetRemoteLogicalName() stringGetLocalLogicalName() stringGetRemoteShosetType() stringGetLocalShosetType() stringGetDir() stringGetRemoteAddress() stringGetLocalAddress() stringGetIsValid() boolWaitForValid()SetConn(conn *tls.Conn)UpdateConn(conn *tls.Conn)SetRemoteLogicalName(lName string)SetLocalAddress(bindAddress string)SetRemoteShosetType(ShosetType string)SetIsValid(state bool)SetRemoteAddress(address string)Store(protocol string, lName string, address string, shosetType string)String() stringHandleConfig(cfg *msg.ConfigProtocol)RunInConnSingle(address string)RunInConnDouble()ReceiveMessage() errorRunPkiRequest(address string) errorHandleSingleWay(messageValue msg.Message) errorHandlePkiRequest(messageValue msg.Message) errorHandlePkiResponse(messageValue msg.Message) errorGetConn() *tls.ConnGetShoset() *ShosetGetReader() *msg.ReaderGetWriter() *msg.WriterGetRemoteLogicalName() stringGetLocalLogicalName() stringGetRemoteShosetType() stringGetLocalShosetType() stringGetDir() stringGetRemoteAddress() stringGetProtocol() stringGetLocalAddress() stringGetIsValid() boolSetConn(conn *tls.Conn)UpdateConn(conn *tls.Conn)SetRemoteLogicalName(lName string)SetLocalAddress(bindAddress string)SetRemoteShosetType(ShosetType string)SetProtocol(protocol string)SetIsValid(state bool)SetRemoteAddress(address string)Store(protocol string, lName string, address string, shosetType string)String() stringHandleConfig(cfg *msg.ConfigProtocol)RunInConnSingle(address string)RunInConnDouble()ReceiveMessage() errorRunPkiRequest(address string) errorHandleSingleWay(messageValue msg.Message) errorHandlePkiRequest(messageValue msg.Message) errorHandlePkiResponse(messageValue msg.Message) errorGetConn() *tls.ConnGetShoset() *ShosetGetReader() *msg.ReaderGetWriter() *msg.WriterGetRemoteLogicalName() stringGetLocalLogicalName() stringGetRemoteShosetType() stringGetLocalShosetType() stringGetDirection() stringGetRemoteAddress() stringGetProtocol() stringGetLocalAddress() stringGetIsValid() boolSetConn(conn *tls.Conn)UpdateConn(conn *tls.Conn)SetRemoteLogicalName(lName string)SetLocalAddress(bindAddress string)SetRemoteShosetType(ShosetType string)SetProtocol(protocol string)SetIsValid(state bool)SetRemoteAddress(address string)Store(protocol string, lName string, address string, shosetType string)String() stringHandleConfig(cfg *msg.ConfigProtocol)RunInConnSingle(address string)RunInConnDouble()ReceiveMessage() errorRunPkiRequest(address string) errorHandleSingleWay(messageValue msg.Message) errorHandlePkiRequest(messageValue msg.Message) errorHandlePkiResponse(messageValue msg.Message) errorGetConn() *tls.ConnGetShoset() *ShosetGetReader() *msg.ReaderGetWriter() *msg.WriterGetRemoteLogicalName() stringGetLocalLogicalName() stringGetRemoteShosetType() stringGetLocalShosetType() stringGetDirection() stringGetRemoteAddress() stringGetProtocol() stringGetLocalAddress() stringGetIsValid() boolSetConn(conn *tls.Conn)UpdateConn(conn *tls.Conn)SetRemoteLogicalName(lName string)SetLocalAddress(bindAddress string)SetRemoteShosetType(ShosetType string)SetProtocol(protocol string)SetIsValid(state bool)SetRemoteAddress(address string)Store(protocol string, lName string, address string, shosetType string)String() stringHandleConfig(cfg *msg.ConfigProtocol)RunInConnSingle(address string)RunInConnDouble()ReceiveMessage() errorRunPkiRequest(address string) errorHandleSingleWay(messageValue msg.Message) errorHandlePkiRequest(messageValue msg.Message) errorHandlePkiResponse(messageValue msg.Message) errorGetConn() *tls.ConnGetShoset() *ShosetGetReader() *msg.ReaderGetWriter() *msg.WriterGetRemoteLogicalName() stringGetLocalLogicalName() stringGetRemoteShosetType() stringGetLocalShosetType() stringGetDirection() stringGetRemoteAddress() stringGetProtocol() stringGetLocalAddress() stringGetIsValid() boolSetConn(conn *tls.Conn)UpdateConn(conn *tls.Conn)SetRemoteLogicalName(lName string)SetLocalAddress(bindAddress string)SetRemoteShosetType(ShosetType string)SetProtocol(protocol string)SetIsValid(state bool)SetRemoteAddress(address string)Store(protocol string, lName string, address string, shosetType string)String() stringHandleConfig(cfg *msg.ConfigProtocol)RunInConnSingle(address string)RunInConnDouble()ReceiveMessage() errorSimpleMessageHandlerGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageforwardAcknownledgeHandlerGet(c *ShosetConn) (msg.Message, error)HandleDoubleWay(c *ShosetConn, message msg.Message) errorSend(s *Shoset, m msg.Message)Wait(s *Shoset, replies *msg.Iterator, argsmap[string]string, timeout int) *msg.MessageViperConcurentSliceListenerConfigConnLoggerShosetCreationLname stringStype stringSrc stringDst []stringPtype stringLaunched boolLname stringShosetType stringLocalAddress stringRemoteAddresses []stringProtocolType stringLaunched boolLname stringShosetType stringLocalAddress stringRemoteAddresses []stringProtocolType stringLaunched boolLname stringShosetType stringLocalAddress stringRemoteAddresses []stringProtocolType stringLaunched boolLname stringShosetType stringLocalAddress stringRemoteAddresses []stringProtocolType stringLaunched boolusesusesusesusesusesusesextendsextendsextendsextendsextendsextendsextendsextendsextendsusesusesusesusesusesusesusesusesusesusesusesusesusesusesusesusesextendsusesimplementsimplementsimplementsimplementsimplementsimplementsimplementsimplementsimplementsimplementsimplementsusesusesusesusesusesusesusesusesusesusesusesusesusesusesusesusesusesusesusesusesusesusesusesalias ofalias of \ No newline at end of file diff --git a/uml/generate_diagrams_svg.sh b/uml/generate_diagrams_svg.sh new file mode 100644 index 0000000..99bf3ab --- /dev/null +++ b/uml/generate_diagrams_svg.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +# Installation instructions : + + +##### Class diagram : #### +# Install goplantuml :(Make sure /home/"user"/go/bin is include in your PATH) +# go install github.com/jfeliu007/goplantuml/cmd/goplantuml@latest + +# Install plantuml : +# (Make sure java is installed) +# Download the jar file from : https://plantuml.com/en/download (Don't forget to change the path to it in the command.) + +goplantuml -recursive -aggregate-private-members -show-aggregations -show-aliases -show-compositions -show-connection-labels -show-implementations -title "Shoset class diagram" ./.. > ./uml/class_diagram_shoset.puml + +java -jar '/media/partagé/DitRit/GoPlantUML/plantuml-1.2022.6.jar' -svg -v ./uml/class_diagram_shoset.puml + + +#### Call diagram : #### +# Install go-callvis : +# git clone https://github.com/ofabry/go-callvis.git +# cd go-callvis && make install + +# Install graphviz (Not sure if it's necessary) : +# sudo apt install graphviz + +go-callvis -nostd -nointer --group pkg,type -minlen 5 -limit github.com/ditrit/shoset -focus github.com/ditrit/shoset -file ./uml/call_graph/shoset ./test + +go-callvis -nostd -nointer --group pkg,type -minlen 5 -limit github.com/ditrit/shoset -focus github.com/ditrit/shoset/msg -file ./uml/call_graph/msg ./test + +go-callvis -nostd -nointer --group pkg,type -minlen 5 -limit github.com/ditrit/shoset -focus github.com/ditrit/shoset/files -file ./uml/call_graph/files ./test #Not working \ No newline at end of file diff --git a/uml/navigable_call_graph.sh b/uml/navigable_call_graph.sh new file mode 100644 index 0000000..da25f17 --- /dev/null +++ b/uml/navigable_call_graph.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +#Open the call graph svg in a browser and generate new graph when you click on a package. +go-callvis -graphviz -nostd -nointer --group pkg,type -limit github.com/ditrit/shoset -focus github.com/ditrit/shoset ./../test #-minlen 50 -nodesep 5 + +#go-callvis -nostd -nointer --group pkg,type -minlen 5 ./../test \ No newline at end of file diff --git a/utils.go b/utils.go index 0d08033..025074a 100644 --- a/utils.go +++ b/utils.go @@ -1,33 +1,151 @@ package shoset import ( + "bytes" + "crypto/rsa" + "crypto/x509" + "encoding/pem" "errors" "fmt" + "io/ioutil" "net" + "os" + "regexp" "strconv" "strings" + "sync" + "time" + + "github.com/howeyc/gopass" + "github.com/square/certstrap/depot" + "github.com/square/certstrap/pkix" + "github.com/urfave/cli" ) -// GetIP : +// fileExists returns true if the path indicated corresponds to an existing file +func fileExists(filepath string) bool { + _, err := os.Stat(filepath) + return !os.IsNotExist(err) +} + +// mkdir creates a repertory if it doesn't already exist +func mkdir(path string) error { + if !fileExists(path) { + return os.MkdirAll(path, 0700) + } + return nil +} + +// contains range through a slice to search for a particular string +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + return false +} + +// removeDuplicateStr range through a slice to delete string duplicates +func removeDuplicateStr(strSlice []string) []string { + allKeys := make(map[string]bool) + list := []string{} + for _, item := range strSlice { + if _, value := allKeys[item]; !value { + allKeys[item] = true + list = append(list, item) + } + } + return list +} + +// Keys returns a []string corresponding to the keys from the map[string]*sync.Map object. +// direction set the specific keys depending on the desired direction. +func Keys(mapSync *sync.Map, direction string) []string { + var keys []string + if direction == ALL { + // all keys whatever the direction from the ShosetConn + mapSync.Range(func(key, _ interface{}) bool { + keys = append(keys, key.(string)) + return true + }) + } else { + // keys with specific ShosetConn direction + mapSync.Range(func(key, value interface{}) bool { + if value.(*ShosetConn).GetDirection() == direction { + keys = append(keys, key.(string)) + } + return true + }) + } + return removeDuplicateStr(keys) +} + +// EncodeFile encodes bytes to a specific encoding type to a specific path +func EncodeFile(object interface{}, encodeType, path string) error { + switch encodeType { + case CERTIFICATE: + file, err := os.Create(path) + if err != nil { + return errors.New("couldn't create cert file : " + err.Error()) + } + defer file.Close() + + err = pem.Encode(file, &pem.Block{Type: encodeType, Bytes: object.([]byte)}) + if err != nil { + return errors.New("couldn't encode in cert file : " + err.Error()) + } + case RSA_PRIVATE_KEY: + file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) + if err != nil { + return errors.New("couldn't open private key file : " + err.Error()) + } + defer file.Close() + + err = pem.Encode(file, &pem.Block{Type: encodeType, Bytes: x509.MarshalPKCS1PrivateKey(object.(*rsa.PrivateKey))}) + if err != nil { + return errors.New("couldn't encode in private key file : " + err.Error()) + } + } + return nil +} + +// GetPrivateKey returns the private key contained in a file +func GetPrivateKey(filePath string) (*rsa.PrivateKey, error) { + CAprivateKeyBytes, err := ioutil.ReadFile(filePath) + if err != nil { + return nil, err + } + block, _ := pem.Decode(CAprivateKeyBytes) + b := block.Bytes + + CAprivateKey, err := x509.ParsePKCS1PrivateKey(b) + if err != nil { + return nil, err + } + return CAprivateKey, nil +} + +// GetIP returns an IPaddress format from an address func GetIP(address string) (string, error) { parts := strings.Split(address, ":") if len(parts) != 2 { - return "", errors.New("address '" + address + "should respect the format hots_name_or_ip:port") + return VOID, errors.New("address '" + address + " should respect the format hots_name_or_ip:port") } hostIps, err := net.LookupHost(parts[0]) if err != nil || len(hostIps) == 0 { - return "", errors.New("address '" + address + "' can not be resolved") + return VOID, errors.New("address '" + address + "' can not be resolved") } port, err := strconv.Atoi(parts[1]) if err != nil { - return "", errors.New("'" + parts[1] + "' is not a port number") + return VOID, errors.New("'" + parts[1] + "' is not a port number") } if port < 1 || port > 65535 { - return "", errors.New("'" + parts[1] + "' is not a valid port number") + return VOID, errors.New("'" + parts[1] + "' is not a valid port number") } host := getV4(hostIps) - if host == "" { - return "", errors.New("failed to get ipv4 address for localhost") + if host == VOID { + return VOID, errors.New("failed to get ipv4 address for localhost") } ipaddr := host + ":" + parts[1] return ipaddr, nil @@ -40,7 +158,7 @@ func getV4(hostIps []string) string { return hostIps[i] } } - return "" + return VOID } // IP2ID : @@ -72,20 +190,171 @@ func DeltaAddress(addr string, portDelta int) (string, bool) { if err == nil { return fmt.Sprintf("%s:%d", parts[0], port+portDelta), true } - return "", false + return VOID, false + } + return VOID, false +} + +// Functions for PKI secret - will be used and documented later +var nowFunc = time.Now + +func parseExpiry(fromNow string) (time.Time, error) { + now := nowFunc().UTC() + re := regexp.MustCompile(`\s*(\d+)\s*(day|month|year|hour|minute|second)s?`) + matches := re.FindAllStringSubmatch(fromNow, -1) + addDate := map[string]int{ + "day": 0, + "month": 0, + "year": 0, + "hour": 0, + "minute": 0, + "second": 0, + } + for _, r := range matches { + number, err := strconv.ParseInt(r[1], 10, 32) + if err != nil { + return now, err + } + addDate[r[2]] = int(number) + } + + // Ensure that we do not overflow time.Duration. + // Doing so is silent and causes signed integer overflow like issues. + if _, err := time.ParseDuration(fmt.Sprintf("%dh", addDate["hour"])); err != nil { + return now, fmt.Errorf("hour unit too large to process") + } else if _, err = time.ParseDuration(fmt.Sprintf("%dm", addDate["minute"])); err != nil { + return now, fmt.Errorf("minute unit too large to process") + } else if _, err = time.ParseDuration(fmt.Sprintf("%ds", addDate["second"])); err != nil { + return now, fmt.Errorf("second unit too large to process") + } + + result := now. + AddDate(addDate["year"], addDate["month"], addDate["day"]). + Add(time.Duration(addDate["hour"]) * time.Hour). + Add(time.Duration(addDate["minute"]) * time.Minute). + Add(time.Duration(addDate["second"]) * time.Second) + + if now == result { + return now, fmt.Errorf("invalid or empty format") + } + + // ASN.1 (encoding format used by SSL) only supports up to year 9999 + // https://www.openssl.org/docs/man1.1.0/crypto/ASN1_TIME_check.html + if result.Year() > 9999 { + return now, fmt.Errorf("proposed date too far in to the future: %s. Expiry year must be less than or equal to 9999", result) + } + + return result, nil +} + +// https://github.com/square/certstrap/tree/master/cmd +var ( + d *depot.FileDepot + depotDir string +) + +// InitDepot creates the depot directory, which stores key/csr/crt files +func InitDepot(path string) error { + depotDir = path + if d == nil { + var err error + if d, err = depot.NewFileDepot(path); err != nil { + return err + } } - return "", false + return nil } -// GetByType : Get shoset by type. -func GetByType(m *MapSafeConn, shosetType string) []*ShosetConn { - var result []*ShosetConn - //m.Lock() - for _, val := range m.GetM() { - if val.GetRemoteShosetType() == shosetType { - result = append(result, val) +func createPassPhrase() ([]byte, error) { + pass1, err := gopass.GetPasswdPrompt("Enter passphrase (empty for no passphrase): ", false, os.Stdin, os.Stdout) + if err != nil { + return nil, err + } + pass2, err := gopass.GetPasswdPrompt("Enter same passphrase again: ", false, os.Stdin, os.Stdout) + if err != nil { + return nil, err + } + + if !bytes.Equal(pass1, pass2) { + return nil, errors.New("Passphrases do not match.") + } + return pass1, nil +} + +func askPassPhrase(name string) ([]byte, error) { + pass, err := gopass.GetPasswdPrompt(fmt.Sprintf("Enter passphrase for %v (empty for no passphrase): ", name), false, os.Stdin, os.Stdout) + if err != nil { + return nil, err + } + return pass, nil +} + +func getPassPhrase(c *cli.Context, name string) ([]byte, error) { + if c.IsSet("passphrase") { + return []byte(c.String("passphrase")), nil + } + return askPassPhrase(name) +} + +func putCertificate(c *cli.Context, d *depot.FileDepot, name string, crt *pkix.Certificate) error { + if c.IsSet("cert") { + bytes, err := crt.Export() + if err != nil { + return err + } + return ioutil.WriteFile(c.String("cert"), bytes, depot.LeafPerm) + } + return depot.PutCertificate(d, name, crt) +} + +func putCertificateSigningRequest(c *cli.Context, d *depot.FileDepot, name string, csr *pkix.CertificateSigningRequest) error { + if c.IsSet("csr") { + bytes, err := csr.Export() + if err != nil { + return err + } + return ioutil.WriteFile(c.String("csr"), bytes, depot.LeafPerm) + } + return depot.PutCertificateSigningRequest(d, name, csr) +} + +func getCertificateSigningRequest(c *cli.Context, d *depot.FileDepot, name string) (*pkix.CertificateSigningRequest, error) { + if c.IsSet("csr") { + bytes, err := ioutil.ReadFile(c.String("csr")) + if err != nil { + return nil, err + } + return pkix.NewCertificateSigningRequestFromPEM(bytes) + } + return depot.GetCertificateSigningRequest(d, name) +} + +func putEncryptedPrivateKey(c *cli.Context, d *depot.FileDepot, name string, key *pkix.Key, passphrase []byte) error { + if c.IsSet("key") { + if fileExists(c.String("key")) { + return nil + } + + bytes, err := key.ExportEncryptedPrivate(passphrase) + if err != nil { + return err + } + return ioutil.WriteFile(c.String("key"), bytes, depot.BranchPerm) + } + return depot.PutEncryptedPrivateKey(d, name, key, passphrase) +} + +func putPrivateKey(c *cli.Context, d *depot.FileDepot, name string, key *pkix.Key) error { + if c.IsSet("key") { + if fileExists(c.String("key")) { + return nil + } + + bytes, err := key.ExportPrivate() + if err != nil { + return err } + return ioutil.WriteFile(c.String("key"), bytes, depot.BranchPerm) } - //m.Unlock() - return result + return depot.PutPrivateKey(d, name, key) }