ably / ably-go

Go client library SDK for Ably realtime messaging service
https://ably.com/download
Apache License 2.0
81 stars 31 forks source link

ably-go should not convert `[]byte` messages to strings #574

Open amnonbc opened 1 year ago

amnonbc commented 1 year ago

The code below send a message of type []byte. But the subscriber receives the message of type string.

https://go.dev/play/p/VUqZbF7B6Wv

2022/12/22 14:03:06 sending message [1 2 3 4] of type []uint8
2022/12/22 14:03:06 Received message  of type: string

According to the Spec (RSL6a) All messages received will be decoded automatically based on the encoding field and the payloads will be converted into the format they were originally sent using i.e. binary, string, or JSON

In this case the message was sent as []byte so should have been received as as []byte.

package main

import (
    "context"
    "github.com/ably/ably-go/ably"
    "log"
    "os"
)

func sub(client *ably.Realtime, done chan struct{}) {
    channel := client.Channels.Get("quickstart")
    _, err := channel.Subscribe(context.Background(), "greeting", func(msg *ably.Message) {
        log.Printf("Received message %v of type: %T", msg.Data, msg.Data)
        close(done)
    })
    if err != nil {
        panic(err)
    }

}

func main() {

    key := os.Getenv("ABLY_KEY")
    client, err := ably.NewRealtime(ably.WithKey(key))
    if err != nil {
        panic(err)
    }
    done := make(chan struct{})
    go sub(client, done)

    channel := client.Channels.Get("quickstart")
    data := []byte{1, 2, 3, 4}
    log.Printf("sending message %v of type %T", data, data)
    err = channel.Publish(context.Background(), "greeting", data)
    if err != nil {
        panic(err)
    }
    <-done
}

┆Issue is synchronized with this Jira Task by Unito

sync-by-unito[bot] commented 1 year ago

➤ Automation for Jira commented:

The link to the corresponding Jira issue is https://ably.atlassian.net/browse/SDK-3196

sync-by-unito[bot] commented 1 year ago

➤ Amnon Cohen commented:

The problem is in the codec, as you can see in this failing test:

func TestSmallByteData(t *testing.T) {
    src := []byte("abc")
    encodedBytes, err := ablyutil.MarshalMsgpack(src)
    assert.NoError(t, err)

    var decoded interface{}
    err = ablyutil.UnmarshalMsgpack(encodedBytes, &decoded)
    assert.NoError(t, err)

    assert.IsType(t, src, decoded)
}
amnonbc commented 1 year ago
func TestUnmarshallByte(t *testing.T) {
    buf := []byte{
        0xc4,     // bin8
        2,        // len
        'a', 'a', // bytes
    }
    var target interface{}

    err := UnmarshalMsgpack(buf, &target)
    require.NoError(t, err)
    assert.IsType(t, []byte{}, target,
        "bin8 should be decoded as []byte, but instead we got %T", target)
}

In github.com/ugorji/go/codec@v1.1.9/msgpack.go:494

        case bd == mpBin8, bd == mpBin16, bd == mpBin32:
            fauxUnionReadRawBytes(d, &d.d, n, d.h.RawToString)

which calls

func fauxUnionReadRawBytes(dr decDriver, d *Decoder, n *fauxUnion, rawToString bool) {
    if rawToString {
        n.v = valueTypeString
        n.s = string(dr.DecodeBytes(d.b[:], true))
    } else {
        n.v = valueTypeBytes
        n.l = dr.DecodeBytes(nil, false)
    }
}
amnonbc commented 1 year ago

I added a bug report to https://github.com/ugorji/go/issues/387

amnonbc commented 1 year ago

See https://github.com/ably/ably-go/pull/581 as a sample fix.