2014-04-02 23:38:36 +00:00
|
|
|
package beam
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"github.com/dotcloud/docker/pkg/beam/data"
|
2014-04-22 23:09:42 +00:00
|
|
|
"io"
|
|
|
|
"os"
|
2014-04-02 23:38:36 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type Router struct {
|
|
|
|
routes []*Route
|
2014-04-22 23:09:42 +00:00
|
|
|
sink Sender
|
2014-04-02 23:38:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewRouter(sink Sender) *Router {
|
2014-04-22 23:09:42 +00:00
|
|
|
return &Router{sink: sink}
|
2014-04-02 23:38:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) Send(payload []byte, attachment *os.File) (err error) {
|
|
|
|
//fmt.Printf("Router.Send(%s)\n", MsgDesc(payload, attachment))
|
|
|
|
defer func() {
|
|
|
|
//fmt.Printf("DONE Router.Send(%s) = %v\n", MsgDesc(payload, attachment), err)
|
|
|
|
}()
|
|
|
|
for _, route := range r.routes {
|
|
|
|
if route.Match(payload, attachment) {
|
|
|
|
return route.Handle(payload, attachment)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if r.sink != nil {
|
2014-04-03 04:58:47 +00:00
|
|
|
// fmt.Printf("[%d] [Router.Send] no match. sending %s to sink %#v\n", os.Getpid(), MsgDesc(payload, attachment), r.sink)
|
2014-04-02 23:38:36 +00:00
|
|
|
return r.sink.Send(payload, attachment)
|
|
|
|
}
|
|
|
|
//fmt.Printf("[Router.Send] no match. return error.\n")
|
|
|
|
return fmt.Errorf("no matching route")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Router) NewRoute() *Route {
|
|
|
|
route := &Route{}
|
|
|
|
r.routes = append(r.routes, route)
|
|
|
|
return route
|
|
|
|
}
|
|
|
|
|
|
|
|
type Route struct {
|
2014-04-22 23:09:42 +00:00
|
|
|
rules []func([]byte, *os.File) bool
|
2014-04-02 23:38:36 +00:00
|
|
|
handler func([]byte, *os.File) error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (route *Route) Match(payload []byte, attachment *os.File) bool {
|
|
|
|
for _, rule := range route.rules {
|
|
|
|
if !rule(payload, attachment) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (route *Route) Handle(payload []byte, attachment *os.File) error {
|
|
|
|
if route.handler == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return route.handler(payload, attachment)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Route) HasAttachment() *Route {
|
|
|
|
r.rules = append(r.rules, func(payload []byte, attachment *os.File) bool {
|
|
|
|
return attachment != nil
|
|
|
|
})
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func (route *Route) Tee(dst Sender) *Route {
|
|
|
|
inner := route.handler
|
|
|
|
route.handler = func(payload []byte, attachment *os.File) error {
|
|
|
|
if inner == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if attachment == nil {
|
|
|
|
return inner(payload, attachment)
|
|
|
|
}
|
|
|
|
// Setup the tee
|
2014-05-09 22:39:55 +00:00
|
|
|
w, err := SendRPipe(dst, payload)
|
2014-04-02 23:38:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
teeR, teeW, err := os.Pipe()
|
|
|
|
if err != nil {
|
|
|
|
w.Close()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
go func() {
|
|
|
|
io.Copy(io.MultiWriter(teeW, w), attachment)
|
|
|
|
attachment.Close()
|
|
|
|
w.Close()
|
|
|
|
teeW.Close()
|
|
|
|
}()
|
|
|
|
return inner(payload, teeR)
|
|
|
|
}
|
|
|
|
return route
|
|
|
|
}
|
|
|
|
|
2014-04-03 04:58:47 +00:00
|
|
|
func (r *Route) Filter(f func([]byte, *os.File) bool) *Route {
|
|
|
|
r.rules = append(r.rules, f)
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2014-04-02 23:38:36 +00:00
|
|
|
func (r *Route) KeyStartsWith(k string, beginning ...string) *Route {
|
|
|
|
r.rules = append(r.rules, func(payload []byte, attachment *os.File) bool {
|
|
|
|
values := data.Message(payload).Get(k)
|
2014-04-03 04:58:47 +00:00
|
|
|
if values == nil {
|
|
|
|
return false
|
|
|
|
}
|
2014-04-02 23:38:36 +00:00
|
|
|
if len(values) < len(beginning) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i, v := range beginning {
|
|
|
|
if v != values[i] {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2014-04-22 23:09:42 +00:00
|
|
|
func (r *Route) KeyEquals(k string, full ...string) *Route {
|
2014-04-03 03:04:33 +00:00
|
|
|
r.rules = append(r.rules, func(payload []byte, attachment *os.File) bool {
|
|
|
|
values := data.Message(payload).Get(k)
|
|
|
|
if len(values) != len(full) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i, v := range full {
|
|
|
|
if v != values[i] {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Route) KeyIncludes(k, v string) *Route {
|
|
|
|
r.rules = append(r.rules, func(payload []byte, attachment *os.File) bool {
|
|
|
|
for _, val := range data.Message(payload).Get(k) {
|
|
|
|
if val == v {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Route) NoKey(k string) *Route {
|
|
|
|
r.rules = append(r.rules, func(payload []byte, attachment *os.File) bool {
|
|
|
|
return len(data.Message(payload).Get(k)) == 0
|
|
|
|
})
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2014-04-03 04:58:47 +00:00
|
|
|
func (r *Route) KeyExists(k string) *Route {
|
|
|
|
r.rules = append(r.rules, func(payload []byte, attachment *os.File) bool {
|
|
|
|
return data.Message(payload).Get(k) != nil
|
|
|
|
})
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2014-04-02 23:38:36 +00:00
|
|
|
func (r *Route) Passthrough(dst Sender) *Route {
|
|
|
|
r.handler = func(payload []byte, attachment *os.File) error {
|
|
|
|
return dst.Send(payload, attachment)
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Route) All() *Route {
|
|
|
|
r.rules = append(r.rules, func(payload []byte, attachment *os.File) bool {
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Route) Handler(h func([]byte, *os.File) error) *Route {
|
|
|
|
r.handler = h
|
|
|
|
return r
|
|
|
|
}
|