It works again

This commit is contained in:
Neale Pickett 2022-06-06 09:54:55 -06:00
parent b45876bcf0
commit 314994adcd
6 changed files with 90 additions and 87 deletions

View File

@ -20,7 +20,7 @@ func TestBook(t *testing.T) {
c1.Expect(1)
// Send to an empty channel
m := Message{0, 0, []uint8{22, 33}}
m := Message{0, 0, []uint16{22, 33}}
b.Send("merf", m)
b.loop()
if c1.Len() > 0 {

View File

@ -1,11 +1,11 @@
package main
import (
"fmt"
"context"
"encoding/json"
"log"
"net/http"
"os"
"strings"
"time"
"nhooyr.io/websocket"
@ -13,6 +13,9 @@ import (
var book Book
const JsonProtocol = "json.vail.woozle.org"
const BinaryProtocol = "binary.vail.woozle.org"
// Clock defines an interface for getting the current time.
//
// We use this in testing to provide a fixed value for the current time, so we
@ -36,51 +39,82 @@ type VailWebSocketConnection struct {
func (c *VailWebSocketConnection) Receive() (Message, error) {
var m Message
var err error
if c.usingJSON {
err = websocket.JSON.Receive(c.Conn, &m)
} else {
buf := make([]byte, 64)
if err := websocket.Message.Receive(c.Conn, &buf); err != nil {
messageType, buf, err := c.Read(context.Background())
if err != nil {
return m, err
}
if err := m.UnmarshalBinary(buf)
if messageType == websocket.MessageText {
err = json.Unmarshal(buf, &m)
} else {
err = m.UnmarshalBinary(buf)
}
return m, err
}
func (c *VailWebSocketConnection) Send(m Message) error {
var err error
var buf []byte
var messageType websocket.MessageType
log.Println("Send", m)
if c.usingJSON {
return websocket.JSON.Send(c.Conn, m)
messageType = websocket.MessageText
buf, err = json.Marshal(m)
} else {
return websocket.Message.Send(c.Conn, m)
messageType = websocket.MessageBinary
buf, err = m.MarshalBinary()
}
log.Println(buf, err)
if err != nil {
return err
}
log.Println("Sending")
return c.Write(context.Background(), messageType, buf)
}
func (c *VailWebSocketConnection) Error(err error) {
msg := fmt.Sprintf("Error: %#v", err)
websocket.JSON.Send(c.Conn, msg)
}
func ChatHandler(w http.ResponseWriter, r *http.Request) {
// Set up websocket
ws, err := websocket.Accept(
w, r,
&websocket.AcceptOptions{
Subprotocols: []string{JsonProtocol, BinaryProtocol},
},
)
if err != nil {
log.Println(err)
return
}
defer ws.Close(websocket.StatusInternalError, "Internal error")
type Client struct {
repeaterName string
usingJSON bool
}
func (c Client) Handle(ws *websocket.Conn) {
sock := &VailWebSocketConnection{
// Create our Vail websocket connection for books to send to
sock := VailWebSocketConnection{
Conn: ws,
usingJSON: c.usingJSON,
}
nowMilli := time.Now().UnixMilli()
ws.MaxPayloadBytes = 50
book.Join(c.repeaterName, sock)
defer book.Part(c.repeaterName, sock)
// websockets apparently sends a subprotocol string, so we can ignore Accept headers!
switch ws.Subprotocol() {
case JsonProtocol:
sock.usingJSON = true
case BinaryProtocol:
sock.usingJSON = false
default:
ws.Close(websocket.StatusPolicyViolation, "client must speak a vail protocol")
return
}
// Join the repeater
repeaterName := r.FormValue("repeater")
book.Join(repeaterName, &sock)
defer book.Part(repeaterName, &sock)
for {
// Read a packet
m, err := sock.Receive()
if err != nil {
sock.Error(err)
log.Println(err)
ws.Close(websocket.StatusInvalidFramePayloadData, err.Error())
break
}
@ -90,34 +124,20 @@ func (c Client) Handle(ws *websocket.Conn) {
}
// If it's wildly out of time, reject it
timeDelta := (nowMilli - m.Timestamp)
timeDelta := (time.Now().UnixMilli() - m.Timestamp)
if timeDelta < 0 {
timeDelta = -timeDelta
}
if timeDelta > 9999 {
fmt.Fprintln(ws, "Bad timestamp")
ws.Close()
return
log.Println(err)
ws.Close(websocket.StatusInvalidFramePayloadData, "Your clock is off by too much")
break
}
book.Send(c.repeaterName, m)
book.Send(repeaterName, m)
}
}
func ChatHandler(w http.ResponseWriter, r *http.Request) {
c := Client{
repeaterName: r.FormValue("repeater"),
}
accept := r.Header.Get("Accept")
if strings.Contains(accept, "json") {
c.usingJSON = true
}
// This API is confusing as hell.
// I suspect there's a better way to do this.
websocket.Handler(c.Handle).ServeHTTP(w, r)
}
func main() {
book = NewBook()
http.Handle("/chat", http.HandlerFunc(ChatHandler))

View File

@ -34,13 +34,13 @@ type Message struct {
// Message timing in ms.
// Timings alternate between tone and silence.
// For example, `A` could be sent as [80, 80, 240]
Duration []uint8
Duration []uint16
}
func NewMessage(ts time.Time, durations ...time.Duration) Message {
msg := Message{
Timestamp: ts.UnixNano() / time.Millisecond.Nanoseconds(),
Duration: make([]uint8, len(durations)),
Duration: make([]uint16, len(durations)),
}
for i, dns := range durations {
ms := dns.Milliseconds()
@ -49,7 +49,7 @@ func NewMessage(ts time.Time, durations ...time.Duration) Message {
} else if ms < 0 {
ms = 0
}
msg.Duration[i] = uint8(ms)
msg.Duration[i] = uint16(ms)
}
return msg
}
@ -69,7 +69,7 @@ func (m Message) MarshalBinary() ([]byte, error) {
return w.Bytes(), nil
}
// Unmarshaling presumes something else is keeping track of lengths
// UnmarshalBinary unpacks a binary buffer into a Message.
func (m *Message) UnmarshalBinary(data []byte) error {
r := bytes.NewReader(data)
if err := binary.Read(r, binary.BigEndian, &m.Timestamp); err != nil {
@ -78,32 +78,14 @@ func (m *Message) UnmarshalBinary(data []byte) error {
if err := binary.Read(r, binary.BigEndian, &m.Clients); err != nil {
return err
}
dlen := r.Len()
m.Duration = make([]uint8, dlen)
dlen := r.Len() / 2
m.Duration = make([]uint16, dlen)
if err := binary.Read(r, binary.BigEndian, &m.Duration); err != nil {
return err
}
return nil
}
func (m Message) MarshalJSON() ([]byte, error) {
buf := new(bytes.Buffer)
fmt.Fprint(buf, "{")
fmt.Fprintf(buf, "\"Timestamp\":%d,", m.Timestamp)
fmt.Fprintf(buf, "\"Clients\":%d,", m.Clients)
fmt.Fprint(buf, "\"Duration\":[")
for i := 0; i < len(m.Duration); i++ {
fmt.Fprint(buf, m.Duration[i])
if i <= len(m.Duration)-1 {
fmt.Fprint(buf, ",")
}
}
fmt.Fprint(buf)
fmt.Fprint(buf, "]")
fmt.Fprint(buf, "}")
return buf.Bytes(), nil
}
func (m Message) Equal(m2 Message) bool {
if m.Timestamp != m2.Timestamp {
return false

View File

@ -7,8 +7,8 @@ import (
)
func TestMessageStruct(t *testing.T) {
m := Message{0x1122334455, 0, []uint8{0xaa, 0xbb, 0xcc}}
m2 := Message{12, 0, []uint8{1}}
m := Message{0x1122334455, 0, []uint16{0xaa, 0xbb, 0xcc}}
m2 := Message{12, 0, []uint16{1}}
if !m.Equal(m) {
t.Error("Equal messages did not compare equal")
@ -16,7 +16,7 @@ func TestMessageStruct(t *testing.T) {
if m.Equal(m2) {
t.Error("Unequal messages compared equal")
}
if m.Equal(Message{m.Timestamp, 0, []uint8{1, 2, 3}}) {
if m.Equal(Message{m.Timestamp, 0, []uint16{1, 2, 3}}) {
t.Error("Messages with different payloads compared equal")
}
@ -24,7 +24,7 @@ func TestMessageStruct(t *testing.T) {
if err != nil {
t.Error(err)
}
if !bytes.Equal(bm, []byte("\x00\x00\x00\x11\x22\x33\x44\x55\x00\x00\xaa\xbb\xcc")) {
if !bytes.Equal(bm, []byte("\x00\x00\x00\x11\x22\x33\x44\x55\x00\x00\x00\xaa\x00\xbb\x00\xcc")) {
t.Error("Encoded wrong:", bm)
}

View File

@ -32,7 +32,7 @@ func (tc *TestingClient) Len() int {
return len(tc.buf)
}
func (tc *TestingClient) Expect(clients uint16, payload ...uint8) {
func (tc *TestingClient) Expect(clients uint16, payload ...uint16) {
m := Message{0, clients, payload}
tc.expected = append(tc.expected, m)
if len(tc.buf) != len(tc.expected) {

View File

@ -45,13 +45,13 @@ export class Vail {
}
console.info("Attempting to reconnect", this.wsUrl.href)
this.clockOffset = 0
this.socket = new WebSocket(this.wsUrl)
this.socket = new WebSocket(this.wsUrl, ["json.vail.woozle.org"])
this.socket.addEventListener("message", e => this.wsMessage(e))
this.socket.addEventListener(
"close",
() => {
console.info("Repeater connection dropped.")
setTimeout(() => this.reopen(), 5*Second)
msg => {
console.error("Repeater connection dropped:", msg.reason)
setTimeout(() => this.reopen(), 2*Second)
}
)
}
@ -72,6 +72,7 @@ export class Vail {
clockOffset: this.clockOffset,
clients: msg.Clients,
}
console.log(msg)
if (typeof(msg) == "string") {
console.error(msg)
return