Skip to content

Commit 9211ad4

Browse files
committed
provide the ability to create OSC servers that only dispatch to methods that match a message's address exactly
1 parent 973e996 commit 9211ad4

8 files changed

+57
-34
lines changed

dispatcher.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,23 @@ type MessageHandler interface {
2929
type Dispatcher map[string]MessageHandler
3030

3131
// Dispatch invokes an OSC bundle's messages.
32-
func (d Dispatcher) Dispatch(b Bundle) error {
32+
func (d Dispatcher) Dispatch(b Bundle, exactMatch bool) error {
3333
var (
3434
now = time.Now()
3535
tt = b.Timetag.Time()
3636
)
3737
if tt.Before(now) {
38-
return d.immediately(b)
38+
return d.immediately(b, exactMatch)
3939
}
4040
<-time.After(tt.Sub(now))
41-
return d.immediately(b)
41+
return d.immediately(b, exactMatch)
4242
}
4343

4444
// immediately invokes an OSC bundle immediately.
45-
func (d Dispatcher) immediately(b Bundle) error {
45+
func (d Dispatcher) immediately(b Bundle, exactMatch bool) error {
4646
for _, p := range b.Packets {
4747
errs := []string{}
48-
if err := d.invoke(p); err != nil {
48+
if err := d.invoke(p, exactMatch); err != nil {
4949
errs = append(errs, err.Error())
5050
}
5151
if len(errs) > 0 {
@@ -57,21 +57,21 @@ func (d Dispatcher) immediately(b Bundle) error {
5757
}
5858

5959
// invoke invokes an OSC packet, which could be a message or a bundle of messages.
60-
func (d Dispatcher) invoke(p Packet) error {
60+
func (d Dispatcher) invoke(p Packet, exactMatch bool) error {
6161
switch x := p.(type) {
6262
case Message:
63-
return d.Invoke(x)
63+
return d.Invoke(x, exactMatch)
6464
case Bundle:
65-
return d.immediately(x)
65+
return d.immediately(x, exactMatch)
6666
default:
6767
return errors.Errorf("unsupported type for dispatcher: %T", p)
6868
}
6969
}
7070

7171
// Invoke invokes an OSC message.
72-
func (d Dispatcher) Invoke(msg Message) error {
72+
func (d Dispatcher) Invoke(msg Message, exactMatch bool) error {
7373
for address, handler := range d {
74-
matched, err := msg.Match(address)
74+
matched, err := msg.Match(address, exactMatch)
7575
if err != nil {
7676
return err
7777
}

dispatcher_test.go

+9-9
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func TestDispatcherDispatchOK(t *testing.T) {
2323
Message{Address: "/bar"},
2424
},
2525
}
26-
if err := d.Dispatch(b); err != nil {
26+
if err := d.Dispatch(b, false); err != nil {
2727
t.Fatal(err)
2828
}
2929
<-c
@@ -43,7 +43,7 @@ func TestDispatcherDispatchError(t *testing.T) {
4343
Message{Address: "/foo"},
4444
},
4545
}
46-
if err := d.Dispatch(b); err == nil {
46+
if err := d.Dispatch(b, false); err == nil {
4747
t.Fatal("expected error, got nil")
4848
}
4949
}
@@ -68,7 +68,7 @@ func TestDispatcherDispatchNestedBundle(t *testing.T) {
6868
},
6969
},
7070
}
71-
if err := d.Dispatch(b); err != nil {
71+
if err := d.Dispatch(b, false); err != nil {
7272
t.Fatal(err)
7373
}
7474
<-c
@@ -83,7 +83,7 @@ func TestDispatcherMiss(t *testing.T) {
8383
b := Bundle{
8484
Timetag: FromTime(time.Now()),
8585
}
86-
if err := d.Dispatch(b); err != nil {
86+
if err := d.Dispatch(b, false); err != nil {
8787
t.Fatal(err)
8888
}
8989
}
@@ -98,20 +98,20 @@ func TestDispatcherInvoke(t *testing.T) {
9898
}),
9999
}
100100
msg := Message{Address: "/foo"}
101-
if err := d.Invoke(msg); err == nil {
101+
if err := d.Invoke(msg, false); err == nil {
102102
t.Fatal("expected error, got nil")
103103
}
104104
badMsg := Message{Address: "/["}
105-
if err := d.Invoke(badMsg); err == nil {
105+
if err := d.Invoke(badMsg, false); err == nil {
106106
t.Fatal("expected error, got nil")
107107
}
108-
if err := d.Invoke(Message{Address: "/bar"}); err != nil {
108+
if err := d.Invoke(Message{Address: "/bar"}, false); err != nil {
109109
t.Fatal(err)
110110
}
111-
if err := d.Invoke(Message{Address: "/baz"}); err != nil {
111+
if err := d.Invoke(Message{Address: "/baz"}, false); err != nil {
112112
t.Fatal(err)
113113
}
114-
if err := d.invoke(badPacket{}); err == nil {
114+
if err := d.invoke(badPacket{}, false); err == nil {
115115
t.Fatal("expected error, got nil")
116116
}
117117
}

message.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ func (msg Message) Equal(other Packet) bool {
8181
}
8282

8383
// Match returns true if the address of the OSC Message matches the given address.
84-
func (msg Message) Match(address string) (bool, error) {
84+
func (msg Message) Match(address string, exactMatch bool) (bool, error) {
85+
if exactMatch {
86+
return address == msg.Address, nil
87+
}
8588
// Verify same number of parts.
8689
if !VerifyParts(address, msg.Address) {
8790
return false, nil

message_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func TestMatch(t *testing.T) {
8585
{"/path/to/m[aei]thod", "/path/to/method"},
8686
} {
8787
msg := Message{Address: pair[0]}
88-
match, err := msg.Match(pair[1])
88+
match, err := msg.Match(pair[1], false)
8989
if err != nil {
9090
t.Fatal(err)
9191
}
@@ -103,7 +103,7 @@ func TestMatch(t *testing.T) {
103103
{"/path/to/[domet]", "/path/to/method"},
104104
} {
105105
msg := Message{Address: pair[0]}
106-
match, err := msg.Match(pair[1])
106+
match, err := msg.Match(pair[1], false)
107107
if err != nil {
108108
t.Fatal(err)
109109
}
@@ -113,7 +113,7 @@ func TestMatch(t *testing.T) {
113113
}
114114

115115
msg := Message{Address: `/[`}
116-
if _, err := msg.Match(`/a`); err == nil {
116+
if _, err := msg.Match(`/a`, false); err == nil {
117117
t.Fatalf("expected error, got nil")
118118
}
119119
}

osc.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ type readSender interface {
128128
read([]byte) (int, net.Addr, error)
129129
}
130130

131-
func serve(r readSender, numWorkers int, dispatcher Dispatcher) error {
131+
func serve(r readSender, numWorkers int, exactMatch bool, dispatcher Dispatcher) error {
132132
if err := checkDispatcher(dispatcher); err != nil {
133133
return err
134134
}
@@ -142,6 +142,7 @@ func serve(r readSender, numWorkers int, dispatcher Dispatcher) error {
142142
Dispatcher: dispatcher,
143143
ErrChan: errChan,
144144
Ready: ready,
145+
ExactMatch: exactMatch,
145146
}.Run()
146147
}
147148
go workerLoop(r, ready, errChan)

udp.go

+13-4
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ type udpConn interface {
1919
type UDPConn struct {
2020
udpConn
2121

22-
closeChan chan struct{}
23-
ctx context.Context
24-
errChan chan error
22+
closeChan chan struct{}
23+
ctx context.Context
24+
errChan chan error
25+
exactMatch bool
2526
}
2627

2728
// DialUDP creates a new OSC connection over UDP.
@@ -111,10 +112,18 @@ func (conn *UDPConn) SendTo(addr net.Addr, p Packet) error {
111112
// Note that this means that errors returned from a dispatcher method will kill your server.
112113
// If context.Canceled or context.DeadlineExceeded are encountered they will be returned directly.
113114
func (conn *UDPConn) Serve(numWorkers int, dispatcher Dispatcher) error {
114-
return serve(conn, numWorkers, dispatcher)
115+
return serve(conn, numWorkers, conn.exactMatch, dispatcher)
115116
}
116117

117118
// SetContext sets the context associated with the conn.
118119
func (conn *UDPConn) SetContext(ctx context.Context) {
119120
conn.ctx = ctx
120121
}
122+
123+
// SetExactMatch changes the behavior of the Serve method so that
124+
// messages will only be dispatched to methods whose addresses
125+
// match the message's address exactly.
126+
// This should provide some performance improvement.
127+
func (conn *UDPConn) SetExactMatch(value bool) {
128+
conn.exactMatch = value
129+
}

unix.go

+13-4
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ type unixConn interface {
2121
type UnixConn struct {
2222
unixConn
2323

24-
closeChan chan struct{}
25-
ctx context.Context
26-
errChan chan error
24+
closeChan chan struct{}
25+
ctx context.Context
26+
errChan chan error
27+
exactMatch bool
2728
}
2829

2930
// DialUnix opens a unix socket for OSC communication.
@@ -111,10 +112,18 @@ func (conn *UnixConn) SendTo(addr net.Addr, p Packet) error {
111112
// Note that this means that errors returned from a dispatcher method will kill your server.
112113
// If context.Canceled or context.DeadlineExceeded are encountered they will be returned directly.
113114
func (conn *UnixConn) Serve(numWorkers int, dispatcher Dispatcher) error {
114-
return serve(conn, numWorkers, dispatcher)
115+
return serve(conn, numWorkers, conn.exactMatch, dispatcher)
115116
}
116117

117118
// TempSocket creates an absolute path to a temporary socket file.
118119
func TempSocket() string {
119120
return filepath.Join(os.TempDir(), ulid.New().String()) + ".sock"
120121
}
122+
123+
// SetExactMatch changes the behavior of the Serve method so that
124+
// messages will only be dispatched to methods whose addresses
125+
// match the message's address exactly.
126+
// This should provide some performance improvement.
127+
func (conn *UnixConn) SetExactMatch(value bool) {
128+
conn.exactMatch = value
129+
}

worker.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ type Worker struct {
1010
Dispatcher Dispatcher
1111
ErrChan chan error
1212
Ready chan<- Worker
13+
ExactMatch bool
1314
}
1415

1516
// Run runs the worker.
@@ -25,15 +26,15 @@ func (w Worker) Run() {
2526
if err != nil {
2627
w.ErrChan <- err
2728
}
28-
if err := w.Dispatcher.Dispatch(bundle); err != nil {
29+
if err := w.Dispatcher.Dispatch(bundle, w.ExactMatch); err != nil {
2930
w.ErrChan <- errors.Wrap(err, "dispatch bundle")
3031
}
3132
case MessageChar:
3233
msg, err := ParseMessage(data, incoming.Sender)
3334
if err != nil {
3435
w.ErrChan <- err
3536
}
36-
if err := w.Dispatcher.Invoke(msg); err != nil {
37+
if err := w.Dispatcher.Invoke(msg, w.ExactMatch); err != nil {
3738
w.ErrChan <- errors.Wrap(err, "dispatch message")
3839
}
3940
default:

0 commit comments

Comments
 (0)