mirror of https://github.com/nealey/vail.git
Channels work now I think
This commit is contained in:
parent
d5f6038670
commit
cdf3869454
|
@ -0,0 +1,92 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Book struct {
|
||||
entries map[string]*Repeater
|
||||
events chan bookEvent
|
||||
}
|
||||
|
||||
|
||||
func NewBook() Book {
|
||||
return Book{
|
||||
entries: make(map[string]*Repeater),
|
||||
events: make(chan bookEvent, 5),
|
||||
}
|
||||
}
|
||||
|
||||
type bookEventType int
|
||||
const (
|
||||
joinEvent = bookEventType(iota)
|
||||
partEvent
|
||||
sendEvent
|
||||
)
|
||||
|
||||
type bookEvent struct {
|
||||
eventType bookEventType
|
||||
name string
|
||||
w io.Writer
|
||||
p []byte
|
||||
}
|
||||
|
||||
func (b Book) Join(name string, w io.Writer) {
|
||||
b.events <- bookEvent{
|
||||
eventType: joinEvent,
|
||||
name: name,
|
||||
w: w,
|
||||
}
|
||||
}
|
||||
|
||||
func (b Book) Part(name string, w io.Writer) {
|
||||
b.events <- bookEvent{
|
||||
eventType: partEvent,
|
||||
name: name,
|
||||
w: w,
|
||||
}
|
||||
}
|
||||
|
||||
func (b Book) Send(name string, p []byte) {
|
||||
b.events <- bookEvent{
|
||||
eventType: sendEvent,
|
||||
name: name,
|
||||
p: p,
|
||||
}
|
||||
}
|
||||
|
||||
func (b Book) Run() {
|
||||
for {
|
||||
b.loop()
|
||||
}
|
||||
}
|
||||
|
||||
func (b Book) loop() {
|
||||
event := <-b.events
|
||||
repeater, ok := b.entries[event.name]
|
||||
|
||||
switch event.eventType {
|
||||
case joinEvent:
|
||||
if ! ok {
|
||||
repeater = NewRepeater()
|
||||
b.entries[event.name] = repeater
|
||||
}
|
||||
repeater.Join(event.w)
|
||||
case partEvent:
|
||||
if ! ok {
|
||||
log.Println("WARN: Parting an empty channel:", event.name)
|
||||
break
|
||||
}
|
||||
repeater.Part(event.w)
|
||||
if len(repeater.subscribers) == 0 {
|
||||
delete(b.entries, event.name)
|
||||
}
|
||||
case sendEvent:
|
||||
if ! ok {
|
||||
log.Println("WARN: Sending to an empty channel:", event.name)
|
||||
break
|
||||
}
|
||||
repeater.Send(event.p)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBook(t *testing.T) {
|
||||
b := NewBook()
|
||||
|
||||
buf1 := bytes.NewBufferString("buf1")
|
||||
b.Join("moo", buf1)
|
||||
b.loop()
|
||||
if len(b.entries) != 1 {
|
||||
t.Error("Wrong number of entries")
|
||||
}
|
||||
|
||||
// Send to an empty channel
|
||||
b.Send("merf", []byte("goober"))
|
||||
b.loop()
|
||||
if buf1.String() != "buf1" {
|
||||
t.Error("Sending to empty channel sent to non-empty channel")
|
||||
}
|
||||
|
||||
// Send to a non-empty channel!
|
||||
b.Send("moo", []byte("goober"))
|
||||
b.loop()
|
||||
if buf1.String() != "buf1goober" {
|
||||
t.Error("Sending didn't work")
|
||||
}
|
||||
|
||||
// Join another client
|
||||
buf2 := bytes.NewBufferString("buf2")
|
||||
b.Join("moo", buf2)
|
||||
b.loop()
|
||||
|
||||
// Send to both
|
||||
b.Send("moo", []byte("snerk"))
|
||||
b.loop()
|
||||
if buf1.String() != "buf1goobersnerk" {
|
||||
t.Error("Send to 2-member channel busted", buf1)
|
||||
}
|
||||
if buf2.String() != "buf2snerk" {
|
||||
t.Error("Send to 2-member channel busted", buf2)
|
||||
}
|
||||
|
||||
// Part a client
|
||||
b.Part("moo", buf1)
|
||||
b.loop()
|
||||
|
||||
b.Send("moo", []byte("peanut"))
|
||||
b.loop()
|
||||
if buf1.String() != "buf1goobersnerk" {
|
||||
t.Error("Parted channel but still getting messages", buf1)
|
||||
}
|
||||
if buf2.String() != "buf2snerkpeanut" {
|
||||
t.Error("Someone else parting somehow messed up sends", buf2)
|
||||
}
|
||||
}
|
30
main.go
30
main.go
|
@ -6,6 +6,22 @@ import (
|
|||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
func (ch Channel) Write(msg Message) {
|
||||
for i, o := range clients {
|
||||
if o.active == false {
|
||||
nclients := len(clients)
|
||||
clients[i] = clients[nclients - 1]
|
||||
clients[nclients - 1] = Client{}
|
||||
clients = clients[:nclients - 1]
|
||||
} else if o == c {
|
||||
// Don't send it back to the sending client
|
||||
} else {
|
||||
o.ws.Write(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
type Client struct {
|
||||
ws *websocket.Conn
|
||||
active bool
|
||||
|
@ -14,6 +30,7 @@ type Client struct {
|
|||
var clients []Client
|
||||
|
||||
func (c Client) Chat() {
|
||||
websocket.Message.Receive
|
||||
for c.active {
|
||||
buf := make([]byte, 800)
|
||||
n, err := c.ws.Read(buf)
|
||||
|
@ -21,19 +38,6 @@ func (c Client) Chat() {
|
|||
c.active = false
|
||||
}
|
||||
buf = buf[:n]
|
||||
|
||||
for i, o := range clients {
|
||||
if o.active == false {
|
||||
nclients := len(clients)
|
||||
clients[i] = clients[nclients - 1]
|
||||
clients[nclients - 1] = Client{}
|
||||
clients = clients[:nclients - 1]
|
||||
} else if o == c {
|
||||
// Don't send it back to the sending client
|
||||
} else {
|
||||
o.ws.Write(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
51
repeater.go
51
repeater.go
|
@ -5,62 +5,31 @@ import (
|
|||
)
|
||||
|
||||
type Repeater struct {
|
||||
joins chan io.Writer
|
||||
parts chan io.Writer
|
||||
sends chan []byte
|
||||
subscribers []io.Writer
|
||||
}
|
||||
|
||||
func NewRepeater() *Repeater {
|
||||
return &Repeater{
|
||||
joins: make(chan io.Writer, 5),
|
||||
parts: make(chan io.Writer, 5),
|
||||
sends: make(chan []byte, 5),
|
||||
subscribers: make([]io.Writer, 0, 20),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repeater) Join(w io.Writer) {
|
||||
r.joins <- w
|
||||
r.subscribers = append(r.subscribers, w)
|
||||
}
|
||||
|
||||
func (r *Repeater) Part(w io.Writer) {
|
||||
r.parts <- w
|
||||
for i, s := range r.subscribers {
|
||||
if s == w {
|
||||
nsubs := len(r.subscribers)
|
||||
r.subscribers[i] = r.subscribers[nsubs-1]
|
||||
r.subscribers = r.subscribers[:nsubs-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repeater) Send(p []byte) {
|
||||
r.sends <- p
|
||||
}
|
||||
|
||||
func (r *Repeater) Close() {
|
||||
close(r.sends)
|
||||
}
|
||||
|
||||
func (r *Repeater) Run() {
|
||||
for r.loop() {}
|
||||
}
|
||||
|
||||
func (r *Repeater) loop() bool {
|
||||
select {
|
||||
case w := <- r.joins:
|
||||
// Add subscriber
|
||||
r.subscribers = append(r.subscribers, w)
|
||||
case w := <- r.parts:
|
||||
// Remove subscriber
|
||||
for i, s := range r.subscribers {
|
||||
if s == w {
|
||||
nsubs := len(r.subscribers)
|
||||
r.subscribers[i] = r.subscribers[nsubs-1]
|
||||
r.subscribers = r.subscribers[:nsubs-1]
|
||||
}
|
||||
}
|
||||
case p, ok := <- r.sends:
|
||||
if ! ok {
|
||||
return false
|
||||
}
|
||||
for _, s := range r.subscribers {
|
||||
s.Write(p)
|
||||
}
|
||||
for _, s := range r.subscribers {
|
||||
s.Write(p)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -10,21 +10,17 @@ func TestRepeater(t *testing.T) {
|
|||
|
||||
buf1 := bytes.NewBufferString("buf1")
|
||||
r.Join(buf1)
|
||||
r.loop()
|
||||
if len(r.subscribers) != 1 {
|
||||
t.Error("Joining did nothing")
|
||||
}
|
||||
r.Send([]byte("moo"))
|
||||
r.loop()
|
||||
if buf1.String() != "buf1moo" {
|
||||
t.Error("Client 1 not repeating", buf1)
|
||||
}
|
||||
|
||||
buf2 := bytes.NewBufferString("buf2")
|
||||
r.Join(buf2)
|
||||
r.loop()
|
||||
r.Send([]byte("bar"))
|
||||
r.loop()
|
||||
if buf1.String() != "buf1moobar" {
|
||||
t.Error("Client 1 not repeating", buf1)
|
||||
}
|
||||
|
@ -33,21 +29,11 @@ func TestRepeater(t *testing.T) {
|
|||
}
|
||||
|
||||
r.Part(buf1)
|
||||
r.loop()
|
||||
r.Send([]byte("baz"))
|
||||
r.loop()
|
||||
if buf1.String() != "buf1moobar" {
|
||||
t.Error("Client 1 still getting data after part", buf1)
|
||||
}
|
||||
if buf2.String() != "buf2barbaz" {
|
||||
t.Error("Client 2 not getting data after part", buf2)
|
||||
}
|
||||
|
||||
r.Close()
|
||||
if r.loop() {
|
||||
t.Error("Closed send didn't terminate loop")
|
||||
}
|
||||
if r.loop() {
|
||||
t.Error("Second loop in terminated channel didn't terminate")
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 72 KiB |
|
@ -7,7 +7,7 @@
|
|||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<img src="1280px-Morse-code-tree.svg.png">
|
||||
<img src="code-tree.png">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
|
Loading…
Reference in New Issue