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, it looks like I'm able to send the payload with WRITEJSON, but what I receive from my Read method is
websocket: close 1006 (abnormal closure): unexpected EOF.
The full terminal message is:
2019/08/04 22:20:31 recv: {"event":"info","version":1} 2019/08/04 22:20:31 recv: {"event":"subscribed","feed":"heartbeat"} 2019/08/04 22:20:31 recv: {"event":"challenge","message":"c6e55c07-d07a-4560-9283-be75ee458433"} ^C2019/08/04 22:21:50 interrupt 2019/08/04 22:21:50 write close: write tcp 192.168.1.6:49624->104.16.51.17:443: write: broken pipe
I understand from here that the status code means that my client closed the connection. I'm unable to track down this problem.
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, handling the error with a close frame, a different OS, increasing the buffer size, and max message size. A lot of it in reference to this post:
https://github.com/gorilla/websocket/issues/321
This is my client:
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 := map[string]interface{}{
"event": "subscribe",
"feed": "fills",
"api_key": "rhsqfT66dxTF7g2O7/t5Cluubjw4MlEz1UoBrZBjf8JocQ/q49j9rH9m",
"original_challenge": challenge,
"signed_challenge": signed,
}
c.Conn.SetWriteDeadline(time.Now().Add(Wait))
err := c.Conn.WriteJSON(fillMessage)
if err != nil {
log.Println("write:", err)
return err
}
return err
}
Here is my main program:
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.Fatalf("socket error: %+v\n", err)
}
log.Printf("recv: %s", payload)
message, signed = ws.Decode(payload)
if err != nil {
log.Fatalf("decode error: %+v\n", err)
}
}
}
I believe that even if the API Keys are wrong a return message should still be sent, as per the API; but instead I get that 1006 close frame.
I'm new to WebSockets. I'm able to get the challenge messaged and heartbeat subscription from the API, the problem is specific to the fill subscription.
Also if anyone wants to replicate this, I can give the full code/Git link.