From 9d47fc3282f5faa0fa8fdd2f0f7bc6deaf375b32 Mon Sep 17 00:00:00 2001 From: 1e99 <1e99@noreply.localhost> Date: Mon, 6 Jan 2025 12:03:58 +0100 Subject: [PATCH] first poc client --- client/main.go | 148 ++++++++++++++++++++++++++++++++++ client/server.go | 27 +++++++ go.mod | 2 +- server/handler.go | 2 + server/storage/storage_mem.go | 4 +- 5 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 client/server.go diff --git a/client/main.go b/client/main.go index aedcbc7..01224aa 100644 --- a/client/main.go +++ b/client/main.go @@ -1,11 +1,159 @@ package main import ( + "bytes" "fmt" + "io" + "math/rand/v2" + "net/http" "os" + "strings" + "time" ) +/* +Connect: +1. Client A: Create URL with public key and ID to reach on +2. Client B: Parses URL, generate AES key, upload key to storage +3. Client A: Polls for storage on server +*/ + +func get(id string) ([]byte, error) { + res, err := http.Get(fmt.Sprintf("http://localhost:3000/store/%s", id)) + if err != nil { + return nil, err + } + + defer res.Body.Close() + + if res.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to get") + } + + data, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + return data, nil +} + +func put(id string, data []byte) error { + req, err := http.NewRequest( + http.MethodPut, + fmt.Sprintf("http://localhost:3000/store/%s", id), + bytes.NewReader(data), + ) + if err != nil { + return err + } + + res, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + + defer res.Body.Close() + if res.StatusCode != http.StatusNoContent { + return fmt.Errorf("failed to get") + } + + return nil +} + +func randomId() string { + var charset = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + + runes := make([]rune, 512) + for i := range runes { + runes[i] = charset[rand.IntN(len(charset))] + } + + return string(runes) +} + +func connect() (string, string) { + // link: chitchat: + var selfId, partnerId string + selfId = randomId() + + fmt.Printf("Your ID: %s\n", selfId) + fmt.Printf("Partner ID: ") + fmt.Scanln(&partnerId) + + partnerId = strings.TrimSpace(partnerId) + return selfId, partnerId +} + +func pollPartner(partnerId string) { + var message string + + for { + time.Sleep(5 * time.Second) + + data, err := get(partnerId) + if err != nil { + fmt.Printf("Failed to poll: %s\n", err) + continue + } + + partnerId, message, _ = strings.Cut(string(data), ":") + fmt.Printf("%s\n", message) + } +} + +func pollInput(selfId string) { + var message string + + for { + fmt.Scanln(&message) + nextId := randomId() + data := fmt.Sprintf("%s:%s", nextId, message) + + err := put(selfId, []byte(data)) + if err != nil { + fmt.Printf("Failed to put: %s\n", err) + continue + } + + selfId = nextId + } +} + func run() error { + selfId, partnerId := connect() + if partnerId == "" { + fmt.Printf("Waiting for partner to connect to you...\n") + + for { + time.Sleep(5 * time.Second) + + data, err := get(selfId) + if err != nil { + fmt.Printf("Partner hasn't connected: %s\n", err) + continue + } + + partnerId = string(data) + break + } + } else { + err := put(partnerId, []byte(selfId)) + if err != nil { + return fmt.Errorf("failed to put next id: %w", err) + } + } + + fmt.Printf("Your ID: %s\n", selfId) + fmt.Printf("Partner ID: %s\n", partnerId) + + fmt.Printf("Chat away!\n") + + go pollInput(partnerId) + go pollPartner(selfId) + for { + } + return nil } diff --git a/client/server.go b/client/server.go new file mode 100644 index 0000000..6bbabc7 --- /dev/null +++ b/client/server.go @@ -0,0 +1,27 @@ +package main + +type Server struct { +} + +// Checks if the storage has an element with the id. +// Returns [ErrNotExists] if the element doesn't exist. +func (s *Server) Has(id string) error { + return nil +} + +// Retreive an element from the storage. +// Returns [ErrNotExists] if the element doesn't exist. +func (s *Server) Get(id string) ([]byte, error) { + return nil, nil +} + +// Store an element in the storage. +func (s *Server) Put(id string, element []byte) error { + return nil +} + +// Delete an element from the storage. +// Returns [ErrNotExists] if the elment doesn't exist. +func (s *Server) Delete(id string) error { + return nil +} diff --git a/go.mod b/go.mod index 84a72a6..3ac651f 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ module git.1e99.eu/1e99/chitchat -go 1.23.4 +go 1.23.3 diff --git a/server/handler.go b/server/handler.go index 42618e3..6e429b4 100644 --- a/server/handler.go +++ b/server/handler.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "io" "net/http" "regexp" @@ -56,6 +57,7 @@ func StoreHandler(store storage.Store, idRegex *regexp.Regexp, maxSize int) http return } + fmt.Printf("Put %s = %s\n", id, string(data)) err = store.Put(id, data) switch { case err != nil: diff --git a/server/storage/storage_mem.go b/server/storage/storage_mem.go index 60c9554..23af36e 100644 --- a/server/storage/storage_mem.go +++ b/server/storage/storage_mem.go @@ -1,6 +1,8 @@ package storage -import "sync" +import ( + "sync" +) func NewMemStore() Store { s := &memStore{