njones / socketio

A Modern SocketIO library for go
MIT License
60 stars 9 forks source link

Number of acknowledgement arguments needs to be equal to number of input arguments? #79

Closed ejjdejong closed 1 year ago

ejjdejong commented 1 year ago

What I'm trying to achieve: I'm setting up a socketio server in Go with this package, and using multiple .net clients to connect and communicate with each other. The .net client is not fully developed yet, so I'm using Postman v10.17 to test the connection with my server (which is running locally at the moment). For my use-case, it's important that some events coming from one side are acknowledged by the other side, with additional arguments. To make it a bit less abstract, an example would be that the client emits an event with one or more arguments, the server processes these arguments, and returns some other arguments in an acknowledgement.

The issue I'm just starting to learn about socketio so I'm not sure if this is the correct way to do this. The code below is how I get to return an acknowledgement from the Go server to Postman:

socket.On("test", callback.FuncAnyAck(func(i ...interface{}) []ser.Serializable {
    fmt.Printf("\nnumber of input arguments: %d", len(i))

    output := []ser.Serializable{}
    returnArgmunents := 2

    for i := 0; i < returnArgmunents; i++ {
        output = append(output, ser.String(fmt.Sprintf("return argument %d", i)))
    }

    return output
}))

The code above does the behavior that I expect when the number of input arguments is equal to the number of output arguments.

However, when I have more input arguments than output arguments, let's say three input vs. two output arguments, there are three arguments being returned instead of two, the last one being null.

And when I have less input arguments than output arguments, the program panics on the CallbackAck function in callback/callback.go:

runtime/debug.Stack()
    /usr/local/go/src/runtime/debug/stack.go:24 +0x7a
panic({0x111a2e0, 0xc000320048})
    /usr/local/go/src/runtime/panic.go:890 +0x267
github.com/njones/socketio/callback.FuncAnyAck.CallbackAck(0x11d6b30, {0xc00034c090, 0x1, 0x3})
    /go/pkg/mod/github.com/njones/socketio@v0.1.4/callback/callback.go:33 +0x2b5

When I change callback/callback.go line 30 from

out := make([]interface{}, len(v))

to

out := make([]interface{}, len(slice))

it does work as expected. However I'm not sure if this is the correct way to go, or that I'm going to mess it up any further. I've searched to find if the number of input and output arguments in an acknowledgement should be equal, but I couldn't find anything about it.

Am I on the right path here regarding ackowledgements? And if so, is the behavior described above correct or am I missing something? Thanks in advance!

ejjdejong commented 1 year ago

I've solved the issue here with creating a custom callback with acknowledgements, similar to the example in the readme. In the custom callback with acknowledgements you can define what values should be returned, meaning the number of input parameters does not have to be the same number of acknowledgement parameters.

For anyone who has the same problem, here's a small example with a custom callback with acknowledgements:

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/njones/socketio"
)

type CustomWrap func(...interface{}) []interface{}

// Callback is called when ackID from the client is 0
func (cc CustomWrap) Callback(v ...interface{}) error {
    slice := cc(v...)

    if len(slice) == 0 {
        return nil
    }

    // check if first arg is error
    err, isOfTypeError := slice[0].(error)
    if !isOfTypeError {
        return nil
    }

    return err
}

// CallbackAck is called when ackID from the client is > 0
func (cc CustomWrap) CallbackAck(v ...interface{}) []interface{} {
    slice := cc(v...)

    return slice
}

func main() {
    port := ":8080"
    server := socketio.NewServer()

    server.OnConnect(func(socket *sio.SocketV4) error {
        socket.On("myEvent", CustomWrap(func(i ...interface{}) []interface{} {
            if len(i) < 2 {
                return []interface{}{fmt.Errorf("not enough input parameters").Error()}
            }

            data, err := doSomething(i[0], i[1])
            if err != nil {
                return []interface{}{err.Error()}
            }

            return []interface{}{data}
        }))
        return nil
    })

    log.Printf("serving port %s...\n", port)
    log.Fatal(http.ListenAndServe(port, server))
}