From b006903a568036f0c55c72e9e93370cebc09e9ae Mon Sep 17 00:00:00 2001 From: Joachim Bauch Date: Thu, 12 Oct 2023 10:33:01 +0200 Subject: [PATCH] Support passing TTL when setting transient data from clients. --- api_signaling.go | 2 ++ docs/standalone-signaling-api-v1.md | 7 +++++-- hub.go | 4 ++-- room.go | 4 ++++ testclient_test.go | 3 ++- transient_data_test.go | 19 +++++++++++++------ 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/api_signaling.go b/api_signaling.go index 1c48c78..db4036b 100644 --- a/api_signaling.go +++ b/api_signaling.go @@ -28,6 +28,7 @@ import ( "net/url" "sort" "strings" + "time" "github.com/golang-jwt/jwt/v4" ) @@ -847,6 +848,7 @@ type TransientDataClientMessage struct { Key string `json:"key,omitempty"` Value *json.RawMessage `json:"value,omitempty"` + TTL time.Duration `json:"ttl,omitempty"` } func (m *TransientDataClientMessage) CheckValid() error { diff --git a/docs/standalone-signaling-api-v1.md b/docs/standalone-signaling-api-v1.md index a53dd26..914cf7d 100644 --- a/docs/standalone-signaling-api-v1.md +++ b/docs/standalone-signaling-api-v1.md @@ -817,14 +817,17 @@ Message format (Client -> Server): "transient": { "type": "set", "key": "sample-key", - "value": "any-json-object" + "value": "any-json-object", + "ttl": "optional-ttl" } } - The `key` must be a string. - The `value` can be of any type (i.e. string, number, array, object, etc.). +- The `ttl` is the time to live in nanoseconds. The value will be removed after + that time (if it is still present). - Requests to set a value that is already present for the key are silently - ignored. + ignored. Any TTL value will be updated / removed. Message format (Server -> Client): diff --git a/hub.go b/hub.go index ee6b1eb..f7ef8fb 100644 --- a/hub.go +++ b/hub.go @@ -1965,9 +1965,9 @@ func (h *Hub) processTransientMsg(client *Client, message *ClientMessage) { } if msg.Value == nil { - room.SetTransientData(msg.Key, nil) + room.SetTransientDataTTL(msg.Key, nil, msg.TTL) } else { - room.SetTransientData(msg.Key, *msg.Value) + room.SetTransientDataTTL(msg.Key, *msg.Value, msg.TTL) } case "remove": if !isAllowedToUpdateTransientData(session) { diff --git a/room.go b/room.go index a4d0e09..e0a533a 100644 --- a/room.go +++ b/room.go @@ -1059,6 +1059,10 @@ func (r *Room) SetTransientData(key string, value interface{}) { r.transientData.Set(key, value) } +func (r *Room) SetTransientDataTTL(key string, value interface{}, ttl time.Duration) { + r.transientData.SetTTL(key, value, ttl) +} + func (r *Room) RemoveTransientData(key string) { r.transientData.Remove(key) } diff --git a/testclient_test.go b/testclient_test.go index 2f2fde7..acb64e6 100644 --- a/testclient_test.go +++ b/testclient_test.go @@ -578,7 +578,7 @@ func (c *TestClient) SendInternalRemoveSession(msg *RemoveSessionInternalClientM return c.WriteJSON(message) } -func (c *TestClient) SetTransientData(key string, value interface{}) error { +func (c *TestClient) SetTransientData(key string, value interface{}, ttl time.Duration) error { payload, err := json.Marshal(value) if err != nil { c.t.Fatal(err) @@ -591,6 +591,7 @@ func (c *TestClient) SetTransientData(key string, value interface{}) error { Type: "set", Key: key, Value: (*json.RawMessage)(&payload), + TTL: ttl, }, } return c.WriteJSON(message) diff --git a/transient_data_test.go b/transient_data_test.go index 7db8483..41ca971 100644 --- a/transient_data_test.go +++ b/transient_data_test.go @@ -148,7 +148,7 @@ func Test_TransientMessages(t *testing.T) { t.Fatal(err) } - if err := client1.SetTransientData("foo", "bar"); err != nil { + if err := client1.SetTransientData("foo", "bar", 0); err != nil { t.Fatal(err) } if msg, err := client1.RunUntilMessage(ctx); err != nil { @@ -202,7 +202,7 @@ func Test_TransientMessages(t *testing.T) { // Client 2 may not modify transient data. session2.SetPermissions([]Permission{}) - if err := client2.SetTransientData("foo", "bar"); err != nil { + if err := client2.SetTransientData("foo", "bar", 0); err != nil { t.Fatal(err) } if msg, err := client2.RunUntilMessage(ctx); err != nil { @@ -213,7 +213,7 @@ func Test_TransientMessages(t *testing.T) { } } - if err := client1.SetTransientData("foo", "bar"); err != nil { + if err := client1.SetTransientData("foo", "bar", 0); err != nil { t.Fatal(err) } @@ -244,7 +244,7 @@ func Test_TransientMessages(t *testing.T) { } // Setting the same value is ignored by the server. - if err := client1.SetTransientData("foo", "bar"); err != nil { + if err := client1.SetTransientData("foo", "bar", 0); err != nil { t.Fatal(err) } ctx2, cancel2 := context.WithTimeout(context.Background(), 100*time.Millisecond) @@ -261,7 +261,7 @@ func Test_TransientMessages(t *testing.T) { data := map[string]interface{}{ "hello": "world", } - if err := client1.SetTransientData("foo", data); err != nil { + if err := client1.SetTransientData("foo", data, 0); err != nil { t.Fatal(err) } @@ -314,7 +314,7 @@ func Test_TransientMessages(t *testing.T) { t.Errorf("Expected no payload, got %+v", msg) } - if err := client1.SetTransientData("abc", data); err != nil { + if err := client1.SetTransientData("abc", data, 10*time.Millisecond); err != nil { t.Fatal(err) } @@ -355,4 +355,11 @@ func Test_TransientMessages(t *testing.T) { }); err != nil { t.Fatal(err) } + + time.Sleep(10 * time.Millisecond) + if msg, err = client3.RunUntilMessage(ctx); err != nil { + t.Fatal(err) + } else if err := checkMessageTransientRemove(msg, "abc", data); err != nil { + t.Fatal(err) + } }