Websocket: Need Help: close 1006 (abnormal closure): unexpected EOF

Created on 31 Jul 2019  路  5Comments  路  Source: gorilla/websocket

Hello!

This is my first issue post so I hope I'm doing this right. If I don't provide enough info or format it the right way, please let me know and I'll fix it ASAP.

I've been trying to subscribe to this API feed- https://www.cryptofacilities.com/resources/hc/en-us/articles/360000578294-Fills

Whenever I try to subscribe, I'm able to send the payload with WRITEJSON, but what I receive is an empty byte slice and EOF.

I've tried running race detection, turning off firewall ( I have a Mac, and tried to turn off the antivirus - found out it's built in. Would this be worth pursuing?) increasing the handshake timeout, running debugger - as per this post:

https://github.com/gorilla/websocket/issues/321

I'm at a loss on next steps, so any help would be appreciated!

Here is my API code:

package websocket

import (
    "crypto/hmac"
    "crypto/sha256"
    "crypto/sha512"
    "encoding/base64"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "time"

    "github.com/gorilla/websocket"
)

var addr = "wss://www.cryptofacilities.com/ws/v1"

var Wait = 50000 * time.Second

var MaxMessageSize int64 = 100000

//WebSocket connection struct to pass into other methods
type WebSocket struct {
    Conn *websocket.Conn
}

//Message represents the public server push messages
type Message struct {
    Event   string
    Feed    string
    Message interface{}
}

type ChallengeSub struct {
    Event   string `json:"event"`
    Message string `json:"message"`
}

type HeartBeat struct {
    Event   string
    Message string
}

type FillSubscribe struct {
    Event             string `json:"event"`
    Feed              string `json:"feed"`
    APIKey            string `json:"api_key"`
    OriginalChallenge string `json:"original_challenge"`
    SignedChallenge   string `json:"signed_challenge"`
}

//OpenWebSocket Connects to kraken API and returns a connection
func OpenWebSocket() (*WebSocket, error) {
    conn, response, err := websocket.DefaultDialer.Dial(addr, nil)
    if err != nil {
        return nil, err
    }
    if response.StatusCode != http.StatusSwitchingProtocols {
        return nil, fmt.Errorf("failed to upgrade protocol to websocket")
    }
    return &WebSocket{Conn: conn}, nil
}

//HeartBeat subscribes to the pingpong feed to keep the connection alive
func (c *WebSocket) HeartBeat() error {

    ping := map[string]interface{}{
        "event": "subscribe",
        "feed":  "heartbeat",
    }
    c.Conn.SetWriteDeadline(time.Now().Add(Wait))
    err := c.Conn.WriteJSON(ping)

    return err
}

func (c *WebSocket) Next() ([]byte, error) {
    _, payload, err := c.Conn.ReadMessage()
    return payload, err
}

//Challenge requests the UUID from kraken API for auth handshake
func (c *WebSocket) Challenge() error {

    challengeRequest := map[string]interface{}{
        "event":   "challenge",
        "api_key": "rhsqfT66dxTF7g2O7/t5Cluubjw4MlEz1UoBrZBjf8JocQ/q49j9rH9m",
    }
    c.Conn.SetWriteDeadline(time.Now().Add(Wait))
    err := c.Conn.WriteJSON(challengeRequest)
    if err != nil {
        log.Println("write:", err)

        return err
    }

    return err
}

func (c *WebSocket) Decode(payload []byte) (string, string) { //Decode takes in a connection and reference to Message struct
    var msg json.RawMessage
    env := Message{
        Message: &msg,
    }
    if err := json.Unmarshal([]byte(payload), &env); err != nil {
        log.Fatal(err)
    }
    switch env.Event {

    case "challenge":
        var s ChallengeSub
        if err := json.Unmarshal(msg, &s.Message); err != nil {
            log.Fatal(err)
        }
        message, signed := c.Signature(s.Message)
        c.FillSubscribe(message, signed)

        return message, signed

    case "info":
        {
            fmt.Println("Connected:", env.Event)
        }
    case "subscribed":
        {
            fmt.Println("Connected to Heartbeat")
        }
    default:
        switch env.Feed {
        case "heartbeat":
            fmt.Println("Live")
        }
    }
    return "No messages to Decode...", ""
}

func (c *WebSocket) Signature(message string) (string, string) {

    secret64, _ := base64.StdEncoding.DecodeString("rhsqfT66dxTF7g2O7/t5Cluubjw4MlEz1UoBrZBjf8JocQ/q49j9rH9m")

    hash := sha256.New()
    hash.Write([]byte(message))

    challenge256 := hash.Sum(nil)

    hmacHash := hmac.New(sha512.New, secret64)
    hmacHash.Write(challenge256)

    secretChallenge := hmacHash.Sum(nil)

    signed := base64.StdEncoding.EncodeToString(secretChallenge)
    return message, signed
}

//FillSubscribe populates message struct and sends out the JSON message
func (c *WebSocket) FillSubscribe(challenge string, signed string) error {

    FillMessage := FillSubscribe{
        Event:             "subscribe",
        Feed:              "fills",
        APIKey:            "wololo",
        OriginalChallenge: challenge,
        SignedChallenge:   signed,
    }
    c.Conn.SetWriteDeadline(time.Now().Add(Wait))
    err := c.Conn.WriteJSON(FillMessage)
    if err != nil {
        log.Println("write:", err)
        return err
    }
    log.Println(err)

    return err
}

And here is my client:

```go
package main

import (
    "fmt"
    "log"

    "github.com/Mjavala/KrakenAPI/websocket"
)

var message string
var signed string

func main() {

    ws, err := websocket.OpenWebSocket()
    if err != nil {
        log.Fatal(err)
    }

    ws.HeartBeat()
    ws.Challenge()

    fmt.Println(message, signed)

    for {
        // We first read in a raw message. An error here is a socket level
        // error.
        payload, err := ws.Next()
        if err != nil {
            log.Panic("socket error: ", err)
        }

        log.Printf("recv: %s\n", payload)

        message, signed = ws.Decode(payload)
        if err != nil {
            log.Fatalf("decode error: %+v\n", err)
        }
    }
}

the terminal result when subscribing using FillSubscribe:

2019/08/08 14:10:18 recv: {"event":"info","version":1} Connected: info
2019/08/08 14:10:19 recv: {"event":"subscribed","feed":"heartbeat"} Connected to Heartbeat
2019/08/08 14:10:19 recv: {"event":"challenge","message":"b7b8759a-4861-46e4-aac5-5aa6e4fa6290"}
2019/08/08 14:10:19 socket error: websocket: close 1006 (abnormal closure): unexpected EOF
panic: socket error: websocket: close 1006 (abnormal closure): unexpected EOF

the terminal result when only subscribing to heartbeat & challenge:

2019/08/08 14:14:51 recv: {"event":"info","version":1} Connected: info
2019/08/08 14:14:51 recv: {"event":"subscribed","feed":"heartbeat"} Connected to Heartbeat
2019/08/08 14:14:51 recv: {"event":"challenge","message":"b20be876-a805-4bf2-9076-693435ac0619"}
b20be876-a805-4bf2-9076-693435ac0619
O6h3rxxDpmFYwxipdjLrIqtKgWDyLYbnxwbUO/p4iuMZLHbmwpyXeTcVD9h6GMrDLOSucBKqMqtK7UPw5fBdCw==
2019/08/08 14:14:59 recv: {"feed":"heartbeat","time":1565295299156} Live
2019/08/08 14:15:09 recv: {"feed":"heartbeat","time":1565295309155} Live
2019/08/08 14:15:19 recv: {"feed":"heartbeat","time":1565295319156} Live

question

Most helpful comment

I've edited your post, @Mjavala, to remove the API key, just in case it was a real key.

All 5 comments

Update: will be trying a few things based on issue #245

@Mjavala is that your real API key (if so you'll want to invalidate it, ASAP)?

looking closely at your terminal output not all the messages you received were empty.
first line: 2019/07/31 15:38:15 recv: {"event":"info","version":1}

that makes me think you're reading empty messages (why they would send empty messages over their websocket I'm not sure but it's still possible); the EOF for a disconnect makes me wonder if your ping handling is working but I'm honestly not sure.

edit: looked again, there's a second line as well,
{"event":"challenge","message":"53bd92e0-a69d-4165-98a0-5a60e1ad22e2"}
are you 100% sure you are handling that challenge?

Obviously, I'm not familiar with the API you're working with but it looks to me like the receive functionality is actually working.

changing log.Printf("recv: %s", message) to log.Printf("recv: %s\n", message) might make reading that easier ;)

I've edited your post, @Mjavala, to remove the API key, just in case it was a real key.

@IngCr3at1on @elithrar Thanks for bringing up the API key. I made sure to delete that key on the exchange after I posted, so no worries :)

I've updated the OP with the latest version of my wrapper and client, as well as the terminal output - I think it's a lot easier to read now.

The only message I receive that is empty is when I try to use the FillSubscribemethod in the Decode function of my API wrapper.

The first terminal output I updated is when that method is used, resulting in the 1006 EOF error.

When I run only the Heartbeat and Challenge methods, everything works as you can see in the 2nd terminal output. I also printed out the unsigned and signed challenge, and the Heartbeat keeps the connection alive no problem!

So I really do think it has to do with the FillSubscribe method, and not a ping pong problem...because the connection is kept alive otherwise? In their sample implementation (Python - https://github.com/CryptoFacilities/WebSocket-v1-Python) they do have a ping interval on the connection, and in the API it is stated that to keep the connection alive a pingpong must happen every 60 seconds...

However...I'm able to keep the connection alive well past 60 seconds using the heartbeat function, and when I do use FillSubscribe the error occurs well before 60 seconds. It doesn't make sense to me that that is the problem, but I'm willing to try anything at this point!

@IngCr3at1on Thanks for the log tip, definitely helps.

Sorry for the (huge) delay here - did you solve this?

Was this page helpful?
0 / 5 - 0 ratings