Golang bufio from websocket breaking after first read

Issue

I am trying to stream JSON text from a websocket. However after an initial read I noticed that the stream seems to break/disconnect. This is from a Pleroma server (think: Mastodon). I am using the default Golang websocket library.

package main

import (
    "bufio"
    "fmt"
    "log"

    "golang.org/x/net/websocket"
)

func main() {
    origin := "https://poa.st/"
    url := "wss://poa.st/api/v1/streaming/?stream=public"

    ws, err := websocket.Dial(url, "", origin)
    if err != nil {
        log.Fatal(err)
    }

    s := bufio.NewScanner(ws)
    for s.Scan() {
        line := s.Text()
        fmt.Println(line)
    }
}

After the initial JSON text response, the for-loop breaks. I would expect it to send a new message every few seconds.

What might be causing this? I am willing to switch to the Gorilla websocket library if I can use it with bufio.

Thanks!

Solution

Although x/net/websocket connection has a Read method with the same signature as the Read method in io.Reader, the connection does not work like an io.Reader. The connection will not work as you expect when wrapped with a bufio.Scanner.

The poa.st endpoint sends a stream of messages where each message is a JSON document. Use the following code to read the messages using the Gorilla package:

url := "wss://poa.st/api/v1/streaming/?stream=public"
ws, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
    log.Fatal(err)
}
defer ws.Close()

for {
    _, p, err := ws.ReadMessage()
    if err != nil {
        log.Fatal(err)
    }
    // p is a []byte containing the JSON document.
    fmt.Printf("%s\n", p)
}

The Gorilla package has a helper method for decoding JSON messages. Here’s an example of how to use that method.

url := "wss://poa.st/api/v1/streaming/?stream=public"
ws, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
    log.Fatal(err)
}
defer ws.Close()

for {
    // The JSON documents are objects containing two fields, 
    // the event type and the payload. The payload is a JSON
    // document itself.
    var e struct {
        Event   string
        Payload string
    }

    err := ws.ReadJSON(&e)
    if err != nil {
        log.Fatal(err)
    }
    // TODO: decode e.Payload based on e.Event
}

Answered By – Bayta Darell

Answer Checked By – Timothy Miller (GoLangFix Admin)

Leave a Reply

Your email address will not be published.