From 174eff6e86004b41f794066eab6c7960407bc233 Mon Sep 17 00:00:00 2001 From: Isak Ruas Date: Wed, 1 May 2024 20:54:02 -0300 Subject: [PATCH] added application example for using the javascript interface. --- app/backend/go.mod | 7 + app/backend/go.sum | 4 + app/backend/main.go | 123 ++++++ app/frontend/index.html | 860 ++++++++++++++++++++++++++++++++++++++++ index.html | 17 - package.json | 2 +- src/algorithms.js | 28 ++ src/algorithms.test.js | 10 + 8 files changed, 1033 insertions(+), 18 deletions(-) create mode 100644 app/backend/go.mod create mode 100644 app/backend/go.sum create mode 100644 app/backend/main.go create mode 100644 app/frontend/index.html delete mode 100644 index.html diff --git a/app/backend/go.mod b/app/backend/go.mod new file mode 100644 index 0000000..f72bdf1 --- /dev/null +++ b/app/backend/go.mod @@ -0,0 +1,7 @@ +module server + +go 1.19 + +require github.com/gorilla/websocket v1.5.1 + +require golang.org/x/net v0.17.0 // indirect diff --git a/app/backend/go.sum b/app/backend/go.sum new file mode 100644 index 0000000..272772f --- /dev/null +++ b/app/backend/go.sum @@ -0,0 +1,4 @@ +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= diff --git a/app/backend/main.go b/app/backend/main.go new file mode 100644 index 0000000..f756077 --- /dev/null +++ b/app/backend/main.go @@ -0,0 +1,123 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" + "sync" + "time" + + "github.com/gorilla/websocket" +) + +type Message struct { + Type string `json:"type"` + Room string `json:"room"` + Text string `json:"text"` +} + +type RoomManager struct { + rooms map[string]map[*websocket.Conn]bool + mutex sync.RWMutex +} + +func NewRoomManager() *RoomManager { + return &RoomManager{ + rooms: make(map[string]map[*websocket.Conn]bool), + } +} + +func (rm *RoomManager) AddClientToRoom(room string, client *websocket.Conn) { + rm.mutex.Lock() + defer rm.mutex.Unlock() + + if rm.rooms[room] == nil { + rm.rooms[room] = make(map[*websocket.Conn]bool) + } + rm.rooms[room][client] = true + log.Printf("[%s] client added to room %s\n", time.Now().UTC().Format(time.RFC3339), room) +} + +func (rm *RoomManager) RemoveClientFromRoom(room string, client *websocket.Conn) { + rm.mutex.Lock() + defer rm.mutex.Unlock() + + if rm.rooms[room] != nil { + delete(rm.rooms[room], client) + if len(rm.rooms[room]) == 0 { + delete(rm.rooms, room) + log.Printf("[%s] room %s is now empty and removed\n", time.Now().UTC().Format(time.RFC3339), room) + } + } +} + +func (rm *RoomManager) BroadcastMessage(room string, message []byte, sender *websocket.Conn) { + rm.mutex.RLock() + defer rm.mutex.RUnlock() + + if rm.rooms[room] != nil { + for client := range rm.rooms[room] { + if client != sender { + go func(c *websocket.Conn) { + if err := c.WriteMessage(websocket.TextMessage, message); err != nil { + log.Printf("[%s] error broadcasting message to client: %v\n", time.Now().UTC().Format(time.RFC3339), err) + rm.RemoveClientFromRoom(room, c) + } + }(client) + } + } + log.Printf("[%s] message broadcasted to room %s\n", time.Now().UTC().Format(time.RFC3339), room) + } +} + +func wsHandler(rm *RoomManager, w http.ResponseWriter, r *http.Request) { + upgrader := websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + // Allow all connections by default + return true + }, + } + + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Printf("[%s] error upgrading to websocket: %v\n", time.Now().UTC().Format(time.RFC3339), err) + return + } + defer conn.Close() + + for { + _, message, err := conn.ReadMessage() + if err != nil { + log.Printf("[%s] error reading message: %v\n", time.Now().UTC().Format(time.RFC3339), err) + rm.RemoveClientFromRoom("", conn) + break + } + + var msg Message + if err := json.Unmarshal(message, &msg); err != nil { + log.Printf("[%s] error unmarshalling message: %v\n", time.Now().UTC().Format(time.RFC3339), err) + continue + } + + switch msg.Type { + case "join": + rm.AddClientToRoom(msg.Room, conn) + case "text": + go rm.BroadcastMessage(msg.Room, message, conn) + case "audio": + go rm.BroadcastMessage(msg.Room, message, conn) + default: + log.Printf("[%s] invalid message type: %s\n", time.Now().UTC().Format(time.RFC3339), msg.Type) + } + } +} + +func main() { + rm := NewRoomManager() + + http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { + wsHandler(rm, w, r) + }) + log.SetFlags(0) + log.Fatal(http.ListenAndServe(":8080", nil)) +} diff --git a/app/frontend/index.html b/app/frontend/index.html new file mode 100644 index 0000000..505c8e1 --- /dev/null +++ b/app/frontend/index.html @@ -0,0 +1,860 @@ + + + + + + + + + + + + + + + +
+ +
+ +

Chat with {{ currentFriend.name }}

+ +
+ +
+ +
+ +

+ {{ renderText(message.raw) }} : +

+ +

You sent an audio message :

+ + +
+ +
+ +

+ : {{ renderText(message.raw) }} +

+ +

: You received an audio message

+ + +
+
+
+ + + +
+ + +
+
+ + +
+ +

Friends List

+ +
    + +
  • + + + + + + + + +
  • +
+
+ + +
+ +

Add/Edit Friend

+ + + + + + + + + + +
+ + +
+ + + + + + {{privateKeyInputError}} + + + +
+ + +
+

Connection lost, attempting to reconnect...

+
+
+ + + + + diff --git a/index.html b/index.html deleted file mode 100644 index cd13f73..0000000 --- a/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - ecutils - - - - - - - - \ No newline at end of file diff --git a/package.json b/package.json index b66c1e7..2610e0c 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "format": "prettier --write \"src/**/*.js\"", "lint": "npx eslint src/**/*.js", "test": "jest dist", - "start": "serve", + "start": "serve app/frontend/", "coverage": "jest dist --coverage", "tree": "tree --prune -I 'node_modules'", "pack": "npx webpack --mode=production --config webpack.config.js && find dist -type f ! -name 'bundle.js' -exec rm -f {} + && rm -rf .babel_cache && rm -rf .jest_cache", diff --git a/src/algorithms.js b/src/algorithms.js index 6ad344f..e705a0d 100644 --- a/src/algorithms.js +++ b/src/algorithms.js @@ -231,4 +231,32 @@ export class Koblitz { } return characters.join('') } + + /** + * Serializes an array of points and their corresponding auxiliary values 'j' into a JSON-friendly representation. + * + * @param {Array>} points - An array of tuples, where each tuple contains a Point object representing a point on the elliptic curve and the auxiliary value 'j' used in its encoding. + * @returns {Array} A new array containing objects with properties `x`, `y`, and `j`. Each object represents the serialized data for a point and its 'j' value. + */ + serialize(points) { + return points.map((point) => { + return { + x: point[0].x.toString(), + y: point[0].y.toString(), + j: point[1].toString(), + } + }) + } + + /** + * Deserializes an array of objects back into an array of tuples containing Point objects and their corresponding auxiliary values 'j'. + * + * @param {Array} serializedPoints - An array of objects with properties `x`, `y`, and `j`. Each object represents the serialized data for a point and its 'j' value. + * @returns {Array>} A new array containing tuples, where each tuple contains a Point object and the auxiliary value 'j' deserialized from the input objects. + */ + deserialize(serializedPoints) { + return serializedPoints.map((item) => { + return [new Point(BigInt(item.x), BigInt(item.y)), BigInt(item.j)] + }) + } } diff --git a/src/algorithms.test.js b/src/algorithms.test.js index b0949d5..4dbcb9f 100644 --- a/src/algorithms.test.js +++ b/src/algorithms.test.js @@ -39,6 +39,16 @@ test('koblitz: encode and decode ascii', () => { expect(decoded_message).toBe(message) }) +test('koblitz: should serialize and deserialize points and auxiliary values', () => { + const message = 'Hello, EC!' + const encoded = encoder.encode(message, 2n ** 8n) + const serialized = encoder.serialize([encoded]) + const deserialized = encoder.deserialize(serialized) + expect(deserialized[0][0].x).toEqual(encoded[0].x) + expect(deserialized[0][0].y).toEqual(encoded[0].y) + expect(deserialized[0][1]).toEqual(encoded[1]) +}) + test('koblitz: encode and decode lengthy ascii', () => { const message = `Morbi nibh dolor, tempus vel arcu eget, sagittis scelerisque mi. Curabitur aliquet tempus odio, vitae rutrum tortor mollis sit amet. Aliquam finibus sapien eu urna efficitur cursus. Nullam ultricies justo et magna molestie, non tincidunt lacus fringilla. Nulla facilisi. Nullam commodo aliquam placerat. Vivamus imperdiet diam id tincidunt ultrices. Nulla vitae odio massa. Nullam rhoncus scelerisque quam vel scelerisque. Duis ac diam quam. Ut volutpat, tellus a vehicula aliquet, ipsum velit maximus ligula, vel fringilla urna libero vel orci. Nulla iaculis tristique sapien in faucibus. Nullam euismod hendrerit sapien, id pulvinar ipsum dictum non. Suspendisse aliquet leo non vulputate lacinia. Mauris sed malesuada sem, sit amet cursus erat. Donec ac cursus dui. Sed at facilisis arcu. Phasellus at pulvinar lorem, tristique fermentum mauris. Donec commodo consequat eros, ut facilisis risus. Donec eget nunc accumsan, scelerisque lectus non, lobortis urna. Mauris non volutpat enim. Curabitur dignissim lorem lacus, elementum luctus odio bibendum at. Praesent rhoncus magna metus, ut vehicula est sodales a. Donec euismod tristique nisl quis sagittis.