mirror of https://github.com/nealey/vail.git
It works again
This commit is contained in:
parent
b45876bcf0
commit
314994adcd
|
@ -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 {
|
||||
|
|
124
cmd/vail/main.go
124
cmd/vail/main.go
|
@ -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)
|
||||
messageType, buf, err := c.Read(context.Background())
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
|
||||
if messageType == websocket.MessageText {
|
||||
err = json.Unmarshal(buf, &m)
|
||||
} else {
|
||||
buf := make([]byte, 64)
|
||||
if err := websocket.Message.Receive(c.Conn, &buf); err != nil {
|
||||
return m, err
|
||||
}
|
||||
if err := m.UnmarshalBinary(buf)
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *VailWebSocketConnection) Error(err error) {
|
||||
msg := fmt.Sprintf("Error: %#v", err)
|
||||
websocket.JSON.Send(c.Conn, msg)
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
repeaterName string
|
||||
usingJSON bool
|
||||
}
|
||||
|
||||
func (c Client) Handle(ws *websocket.Conn) {
|
||||
sock := &VailWebSocketConnection{
|
||||
Conn: ws,
|
||||
usingJSON: c.usingJSON,
|
||||
log.Println(buf, err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nowMilli := time.Now().UnixMilli()
|
||||
ws.MaxPayloadBytes = 50
|
||||
book.Join(c.repeaterName, sock)
|
||||
defer book.Part(c.repeaterName, sock)
|
||||
|
||||
log.Println("Sending")
|
||||
return c.Write(context.Background(), messageType, buf)
|
||||
}
|
||||
|
||||
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")
|
||||
|
||||
// Create our Vail websocket connection for books to send to
|
||||
sock := VailWebSocketConnection{
|
||||
Conn: ws,
|
||||
}
|
||||
|
||||
// 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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue